DSA - Data Structures and Algorithms
Loading...
Searching...
No Matches
forward_list.h
Go to the documentation of this file.
1
11
12#ifndef FORWARD_LIST_H
13#define FORWARD_LIST_H
14
15#include <cstddef>
16#include <functional>
17#include <initializer_list>
18#include <iostream>
19#include <iterator>
20#include <limits>
21#include <memory>
22#include <ranges>
23#include <utility>
24
25namespace dsa
26{
27
28 template<typename T>
29 class ForwardList;
30
37 template<typename T>
39 {
40 public:
41
46 {
47 public:
48
52 NodeBase() = default;
53
59 NodeBase(const NodeBase& other) = default;
60
67 auto operator=(const NodeBase& other) -> NodeBase & = default;
68
75 NodeBase(NodeBase&& other) = default;
76
84 auto operator=(NodeBase&& other) -> NodeBase & = default;
85
89 virtual ~NodeBase() = default;
90
91 private:
92
93 friend class ForwardList<T>;
94
98 NodeBase* m_next{};
99 };
100
104 class Node : public NodeBase
105 {
106 public:
107
113 Node(const T& value)
114 : m_value{ value }
115 {}
116
124 : m_value{ std::move(value) }
125 {}
126
132 [[nodiscard]] auto value() -> T&
133 {
134 return m_value;
135 }
136
142 [[nodiscard]] auto value() const -> T
143 {
144 return m_value;
145 }
146
147 private:
148
152 friend class ForwardList;
153
154 T m_value{};
155 };
156
166 template<bool IF_CONST>
168 {
169 public:
170
177 using iterator_type = std::conditional_t<IF_CONST, const T, T>;
178
185 using iterator_category = std::forward_iterator_tag;
186
192 using difference_type = std::ptrdiff_t;
193
199 using value_type = T;
200
206 using size_type = std::size_t;
207
214
219
223 ForwardListIterator() noexcept = default;
224
231 : m_current_node{ node }
232 {}
233
241 {
242 this->m_current_node = node;
243 return *this;
244 }
245
252 {
253 if (m_current_node)
254 {
255 m_current_node = m_current_node->m_next;
256 }
257
258 return *this;
259 }
260
267 {
268 const ForwardListIterator iterator = *this;
269 ++(*this);
270 return iterator;
271 }
272
281 auto operator==(const ForwardListIterator<IF_CONST>& other) const -> bool
282 {
283 return m_current_node == other.m_current_node;
284 }
285
294 auto operator!=(const ForwardListIterator<IF_CONST>& other) const -> bool
295 {
296 return !operator==(other);
297 }
298
304 auto operator*() const -> reference
305 {
306 if (Node* node = dynamic_cast<Node*>(m_current_node))
307 {
308 return node->value();
309 }
310 throw std::runtime_error("Invalid iterator dereference");
311 }
312
319 {
320 if (Node* node = dynamic_cast<Node*>(m_current_node))
321 {
322 return &node->value();
323 }
324 throw std::runtime_error("Invalid iterator pointer");
325 }
326
331 {
332 return ForwardListIterator<true>(m_current_node);
333 }
334
335 private:
336
340 friend class ForwardList;
341
342 NodeBase* m_current_node{};
343 };
344
350 using value_type = T;
351
355 using allocator_type = std::allocator<value_type>;
356
362 using size_type = std::size_t;
363
369 using difference_type = std::ptrdiff_t;
370
377
383 using const_pointer = const value_type*;
384
391
398
403
408
412 ForwardList();
413
420 ForwardList(size_type count);
421
429 ForwardList(size_type count, const T& value);
430
438 template<typename InputIt>
439 requires std::input_iterator<InputIt>
440 ForwardList(InputIt first, InputIt last);
441
447 ForwardList(const std::initializer_list<T>& init_list);
448
454 ForwardList(const ForwardList<T>& other);
455
462 auto operator=(const ForwardList<T>& other) -> ForwardList&;
463
470 ForwardList(ForwardList<T>&& other) noexcept;
471
479 auto operator=(ForwardList<T>&& other) noexcept -> ForwardList&;
480
484 ~ForwardList();
485
492 void assign(size_type count, const_reference value);
493
501 template<typename InputIt>
502 requires std::input_iterator<InputIt>
503 void assign(InputIt first, InputIt last);
504
510 void assign(const std::initializer_list<T>& init_list);
511
517 [[nodiscard]] constexpr auto get_allocator() const -> allocator_type;
518
524 [[nodiscard]] auto front() -> reference;
525
531 [[nodiscard]] auto front() const -> const_reference;
532
538 [[nodiscard]] auto before_begin() noexcept -> iterator;
539
545 [[nodiscard]] auto before_begin() const noexcept -> const_iterator;
546
552 [[nodiscard]] auto cbefore_begin() const noexcept -> const_iterator;
553
559 [[nodiscard]] auto begin() noexcept -> iterator;
560
566 [[nodiscard]] auto begin() const noexcept -> const_iterator;
567
573 [[nodiscard]] auto cbegin() const noexcept -> const_iterator;
574
580 [[nodiscard]] auto end() noexcept -> iterator;
581
587 [[nodiscard]] auto end() const noexcept -> const_iterator;
588
594 [[nodiscard]] auto cend() const noexcept -> const_iterator;
595
602 [[nodiscard]] auto empty() const -> bool;
603
609 [[nodiscard]] auto max_size() const noexcept -> size_type;
610
614 void clear();
615
625 auto insert_after(const const_iterator& pos, const_reference value) -> iterator;
626
636 auto insert_after(const const_iterator& pos, T&& value) -> iterator;
637
648 auto insert_after(const const_iterator& pos, size_type count, const_reference value) -> iterator;
649
660 template<typename InputIt>
661 requires std::input_iterator<InputIt>
662 auto insert_after(const const_iterator& pos, InputIt first, InputIt last) -> iterator;
663
673 auto insert_after(const const_iterator& pos, std::initializer_list<T> init_list) -> iterator;
674
687 template<typename... Args>
688 auto emplace_after(const_iterator pos, Args&&... args) -> iterator;
689
698 auto erase_after(const const_iterator& pos) -> iterator;
699
709 auto erase_after(const const_iterator& first, const const_iterator& last) -> iterator;
710
720 void push_front(const_reference value);
721
731 void push_front(T&& value);
732
744 template<typename... Args>
745 auto emplace_front(Args&&... args) -> reference;
746
750 void pop_front();
751
757 void resize(size_type count);
758
765 void resize(size_type count, const_reference value);
766
772 void swap(ForwardList<T>& other) noexcept(std::is_nothrow_swappable_v<T>);
773
784 void merge(ForwardList<T>& other);
785
796 void merge(ForwardList<T>&& other);
797
809 template<typename Compare>
810 void merge(ForwardList<T>& other, Compare comp);
811
823 template<typename Compare>
824 // transfers ownership of nodes, moving entire container is not necessary
825 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
826 void merge(ForwardList<T>&& other, Compare comp);
827
839 void splice_after(const const_iterator& pos, ForwardList<T>& other);
840
852 void splice_after(const_iterator pos, ForwardList<T>&& other);
853
866 void splice_after(const const_iterator& pos, ForwardList<T>& other, const const_iterator& iter);
867
880 void splice_after(const_iterator pos, ForwardList<T>&& other, const_iterator iter);
881
895 void splice_after(const const_iterator& pos, ForwardList<T>& other,
896 const const_iterator& first, const const_iterator& last);
897
911 void splice_after(const_iterator pos, ForwardList<T>&& other, const_iterator first, const_iterator last);
912
921 auto remove(const_reference value) -> size_type;
922
932 template<typename UnaryPred>
933 auto remove_if(UnaryPred predicate) -> size_type;
934
938 void reverse();
939
946 auto unique() -> size_type;
947
956 template<typename BinaryPred>
957 auto unique(BinaryPred predicate) -> size_type;
958
967 void sort();
968
980 template<typename Compare>
981 void sort(Compare comp);
982
988 [[nodiscard]] auto size() const -> size_type
989 {
990 return m_size;
991 }
992
993 private:
994
999 void init_node();
1000
1006 [[nodiscard]] auto find_iter_before_last() -> iterator;
1007
1014 auto erase_element_after(iterator pos) -> iterator;
1015
1024 template<typename... Args>
1025 auto construct_node(NodeBase* next_ptr, Args&&... args) -> Node*;
1026
1032 void destroy_node(NodeBase* node_to_destroy);
1033
1042 [[nodiscard]] auto if_valid_iterator(const const_iterator& pos) -> bool;
1043
1052 [[nodiscard]] auto distance(const_iterator first, const const_iterator& last) -> size_type;
1053
1062 // transfers ownership of nodes, moving entire container is not necessary
1063 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
1064 void transfer(const const_iterator& pos, ForwardList<T>&& other, const const_iterator& iter);
1065
1075 // transfers ownership of nodes, moving entire container is not necessary
1076 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
1077 void transfer(const const_iterator& pos, ForwardList<T>&& other,
1078 const const_iterator& first, const const_iterator& last);
1079
1089 template<typename Compare>
1090 auto merge_sort(NodeBase* source, Compare comp) -> NodeBase*;
1091
1101 template<typename Compare>
1102 auto merge(NodeBase* left, NodeBase* right, Compare comp) -> NodeBase*;
1103
1104 NodeBase* m_head{};
1105 size_type m_size{};
1106
1110 allocator_type m_allocator{};
1111
1115 using node_allocator = typename std::allocator_traits<std::allocator<T>>::template rebind_alloc<Node>;
1116
1120 using node_alloc_traits = std::allocator_traits<node_allocator>;
1121
1125 node_allocator node_alloc{};
1126 };
1127
1128 template<typename T>
1130 {
1131 init_node();
1132 }
1133
1134 template<typename T>
1136 : ForwardList(count, T{})
1137 {}
1138
1139 template<typename T>
1141 {
1142 init_node();
1143
1144 for (size_type i = 0; i < count; i++)
1145 {
1146 push_front(value);
1147 }
1148 }
1149
1150 template<typename T>
1151 template<typename InputIt>
1152 requires std::input_iterator<InputIt>
1153 ForwardList<T>::ForwardList(InputIt first, InputIt last)
1154 {
1155 init_node();
1156
1157 assign(first, last);
1158 }
1159
1160 template<typename T>
1161 ForwardList<T>::ForwardList(const std::initializer_list<T>& init_list)
1162 {
1163 init_node();
1164
1165 auto iter = before_begin();
1166 for (const auto& item : init_list)
1167 {
1168 iter = insert_after(iter, item);
1169 }
1170 }
1171
1172 template<typename T>
1174 {
1175 init_node();
1176
1177 auto iter = before_begin();
1178 for (const auto& item : other)
1179 {
1180 iter = insert_after(iter, item);
1181 }
1182 }
1183
1184 template<typename T>
1186 {
1187 init_node();
1188
1189 if (&other != this)
1190 {
1191 while (m_head->m_next)
1192 {
1193 pop_front();
1194 }
1195
1196 auto iter = before_begin();
1197 for (const auto& item : other)
1198 {
1199 iter = insert_after(iter, item);
1200 }
1201 }
1202
1203 return *this;
1204 }
1205
1206 template<typename T>
1208 {
1209 operator=(std::move(other));
1210 }
1211
1212 template<typename T>
1214 {
1215 if (&other != this)
1216 {
1217 clear();
1218 init_node();
1219
1220 m_head->m_next = other.m_head->m_next;
1221 m_size = other.m_size;
1222
1223 other.m_head->m_next = nullptr;
1224 other.m_size = 0;
1225 }
1226
1227 return *this;
1228 }
1229
1230 template<typename T>
1232 {
1233 clear();
1234 delete m_head;
1235 }
1236
1237 template<typename T>
1239 {
1240 while (m_head->m_next)
1241 {
1242 pop_front();
1243 }
1244
1245 auto iter = before_begin();
1246 for (size_type i = 0; i < count; i++)
1247 {
1248 iter = insert_after(iter, value);
1249 }
1250 }
1251
1252 template<typename T>
1253 template<typename InputIt>
1254 requires std::input_iterator<InputIt>
1255 void ForwardList<T>::assign(InputIt first, InputIt last)
1256 {
1257 clear();
1258
1259 auto iter = before_begin();
1260 while (first != last)
1261 {
1262 iter = insert_after(iter, *first);
1263 first++;
1264 }
1265 }
1266
1267 template<typename T>
1268 void ForwardList<T>::assign(const std::initializer_list<T>& init_list)
1269 {
1270 while (m_head->m_next)
1271 {
1272 pop_front();
1273 }
1274
1275 auto iter = before_begin();
1276 for (const auto& item : init_list)
1277 {
1278 iter = insert_after(iter, item);
1279 }
1280 }
1281
1282 template<typename T>
1283 [[nodiscard]] constexpr auto ForwardList<T>::get_allocator() const -> allocator_type
1284 {
1285 return m_allocator;
1286 }
1287
1288 template<typename T>
1290 {
1291 return *begin();
1292 }
1293
1294 template<typename T>
1296 {
1297 return *cbegin();
1298 }
1299
1300 template<typename T>
1301 auto ForwardList<T>::before_begin() noexcept -> typename ForwardList<T>::iterator
1302 {
1303 return iterator(m_head);
1304 }
1305
1306 template<typename T>
1307 auto ForwardList<T>::before_begin() const noexcept -> typename ForwardList<T>::const_iterator
1308 {
1309 return const_iterator(m_head);
1310 }
1311
1312 template<typename T>
1313 auto ForwardList<T>::cbefore_begin() const noexcept -> typename ForwardList<T>::const_iterator
1314 {
1315 return before_begin();
1316 }
1317
1318 template<typename T>
1319 auto ForwardList<T>::begin() noexcept -> typename ForwardList<T>::iterator
1320 {
1321 return iterator(m_head->m_next);
1322 }
1323
1324 template<typename T>
1325 auto ForwardList<T>::begin() const noexcept -> typename ForwardList<T>::const_iterator
1326 {
1327 return const_iterator(m_head->m_next);
1328 }
1329
1330 template<typename T>
1331 auto ForwardList<T>::cbegin() const noexcept -> typename ForwardList<T>::const_iterator
1332 {
1333 return begin();
1334 }
1335
1336 template<typename T>
1337 auto ForwardList<T>::end() noexcept -> typename ForwardList<T>::iterator
1338 {
1339 return iterator(nullptr);
1340 }
1341
1342 template<typename T>
1343 auto ForwardList<T>::end() const noexcept -> typename ForwardList<T>::const_iterator
1344 {
1345 return const_iterator(nullptr);
1346 }
1347
1348 template<typename T>
1349 auto ForwardList<T>::cend() const noexcept -> typename ForwardList<T>::const_iterator
1350 {
1351 return end();
1352 }
1353
1354
1355 template<typename T>
1356 auto ForwardList<T>::empty() const -> bool
1357 {
1358 return m_size == 0;
1359 }
1360
1361 template<typename T>
1362 auto ForwardList<T>::max_size() const noexcept -> size_type
1363 {
1364 return std::numeric_limits<size_type>::max();
1365 }
1366
1367 template<typename T>
1369 {
1370 if (m_head)
1371 {
1372 NodeBase* temp{ m_head->m_next };
1373 while (temp)
1374 {
1375 m_head->m_next = temp->m_next;
1376 destroy_node(temp);
1377 temp = m_head->m_next;
1378 }
1379
1380 m_size = 0;
1381 m_head->m_next = nullptr;
1382 }
1383 }
1384
1385 template<typename T>
1387 -> typename ForwardList<T>::iterator
1388 {
1389 return insert_after(pos, 1, value);
1390 }
1391
1392 template<typename T>
1394 -> typename ForwardList<T>::iterator
1395 {
1396 if (!if_valid_iterator(pos))
1397 {
1398 return nullptr;
1399 }
1400
1401 iterator iter{ pos.m_current_node };
1402 iter = emplace_after(iter, std::move(value));
1403
1404 return iter;
1405 }
1406
1407 template<typename T>
1409 -> typename ForwardList<T>::iterator
1410 {
1411 if (!if_valid_iterator(pos))
1412 {
1413 return nullptr;
1414 }
1415
1416 iterator iter{ pos.m_current_node };
1417 for (size_type i = 0; i < count; i++)
1418 {
1419 iter = emplace_after(iter, value);
1420 }
1421
1422 return iter;
1423 }
1424
1425 template<typename T>
1426 template<typename InputIt>
1427 requires std::input_iterator<InputIt>
1428 auto ForwardList<T>::insert_after(const const_iterator& pos, InputIt first, InputIt last)
1429 -> typename ForwardList<T>::iterator
1430 {
1431 if (!if_valid_iterator(pos))
1432 {
1433 return nullptr;
1434 }
1435
1436 iterator iter{ pos.m_current_node };
1437 while (first != last)
1438 {
1439 iter = insert_after(iter, *first);
1440 first++;
1441 }
1442
1443 return iter;
1444 }
1445
1446 template<typename T>
1447 auto ForwardList<T>::insert_after(const const_iterator& pos, std::initializer_list<T> init_list)
1448 -> typename ForwardList<T>::iterator
1449 {
1450 if (!if_valid_iterator(pos))
1451 {
1452 return nullptr;
1453 }
1454
1455 iterator iter{ pos.m_current_node };
1456 for (const auto val : init_list)
1457 {
1458 iter = emplace_after(iter, val);
1459 }
1460
1461 return iter;
1462 }
1463
1464 template<typename T>
1465 template<typename... Args>
1467 {
1468 const iterator current{ pos.m_current_node };
1469
1470 Node* newNode{ construct_node(current.m_current_node->m_next, std::forward<Args>(args)...) };
1471 current.m_current_node->m_next = newNode;
1472 ++m_size;
1473
1474 return iterator(newNode);
1475 }
1476
1477 template<typename T>
1479 {
1480 if (!if_valid_iterator(pos))
1481 {
1482 return nullptr;
1483 }
1484
1485 iterator iter{ pos.m_current_node };
1486 iter = erase_element_after(iter);
1487
1488 return iter;
1489 }
1490
1491 template<typename T>
1493 const const_iterator& last) -> typename ForwardList<T>::iterator
1494 {
1495 if (!if_valid_iterator(first) || !if_valid_iterator(last))
1496 {
1497 return nullptr;
1498 }
1499
1500 const iterator iter(first.m_current_node);
1501 const size_type dist = distance(first, last);
1502 for (size_type i = 0; i < dist - 1; i++)
1503 {
1504 erase_element_after(iter);
1505 }
1506
1507 return first.m_current_node->m_next;
1508 }
1509
1510 template<typename T>
1515
1516 template<typename T>
1518 {
1519 emplace_after(before_begin(), std::move(value));
1520 }
1521
1522 template<typename T>
1523 template<typename... Args>
1525 {
1526 emplace_after(before_begin(), std::forward<Args>(args)...);
1527 return front();
1528 }
1529
1530 template<typename T>
1532 {
1533 if (!m_head->m_next)
1534 {
1535 return;
1536 }
1537
1538 NodeBase* temp{ m_head->m_next };
1539 if (m_size == 1)
1540 {
1541 m_head->m_next = nullptr;
1542 }
1543 else
1544 {
1545 m_head->m_next = temp->m_next;
1546 }
1547 destroy_node(temp);
1548
1549 m_size--;
1550 }
1551
1552 template<typename T>
1554 {
1555 resize(count, T{});
1556 }
1557
1558 template<typename T>
1560 {
1561 init_node();
1562
1563 if (count == m_size)
1564 {
1565 return;
1566 }
1567
1568 if (m_size > count)
1569 {
1570 auto iter = begin();
1571 for (size_type i = 0; i < count - 1; i++)
1572 {
1573 ++iter;
1574 }
1575
1576 while (m_size > count)
1577 {
1578 erase_after(iter);
1579 }
1580 }
1581
1582 if (m_size < count)
1583 {
1584 auto iter = find_iter_before_last();
1585 if (iter != before_begin())
1586 {
1587 ++iter;
1588 }
1589
1590 while (m_size < count)
1591 {
1592 iter = insert_after(iter, value);
1593 }
1594 }
1595 }
1596
1597 template<typename T>
1598 void ForwardList<T>::swap(ForwardList<T>& other) noexcept(std::is_nothrow_swappable_v<T>)
1599 {
1600 std::swap(m_head->m_next, other.m_head->m_next);
1601 std::swap(m_size, other.m_size);
1602 }
1603
1604 template<typename T>
1606 {
1607 merge(std::move(other));
1608 }
1609
1610 template<typename T>
1612 {
1613 merge(std::move(other), std::less<>());
1614 }
1615
1616 template<typename T>
1617 template<typename Compare>
1618 void ForwardList<T>::merge(ForwardList<T>& other, Compare comp)
1619 {
1620 merge(std::move(other), comp);
1621 }
1622
1623 template<typename T>
1624 template<typename Compare>
1625 // transfers ownership of nodes, moving entire container is not necessary
1626 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
1627 void ForwardList<T>::merge(ForwardList<T>&& other, Compare comp)
1628 {
1629 if (&other == this)
1630 {
1631 return;
1632 }
1633
1634 if (m_size != 0)
1635 {
1636 Node* temp_head{ construct_node(nullptr, T{}) };
1637 NodeBase* temp_tail{ temp_head };
1638
1639 NodeBase* to_move{};
1640 NodeBase* to_return{};
1641
1642 while (m_head->m_next && other.m_head->m_next)
1643 {
1644 Node* node_this = dynamic_cast<Node*>(m_head->m_next);
1645 Node* node_other = dynamic_cast<Node*>(other.m_head->m_next);
1646
1647 if (node_this && node_other)
1648 {
1649 if (comp(node_this->value(), node_other->value()))
1650 {
1651 to_move = m_head->m_next;
1652 to_return = to_move->m_next;
1653 temp_tail->m_next = to_move;
1654 m_head->m_next = to_return;
1655 }
1656 else
1657 {
1658 to_move = other.m_head->m_next;
1659 to_return = to_move->m_next;
1660 temp_tail->m_next = to_move;
1661 other.m_head->m_next = to_return;
1662 }
1663
1664 temp_tail = temp_tail->m_next;
1665 }
1666 }
1667
1668 if (m_head->m_next == nullptr)
1669 {
1670 temp_tail->m_next = other.m_head->m_next;
1671 other.m_head->m_next = nullptr;
1672 }
1673 else
1674 {
1675 temp_tail->m_next = m_head->m_next;
1676 m_head->m_next = nullptr;
1677 }
1678
1679 m_head->m_next = temp_head->m_next;
1680 destroy_node(temp_head);
1681
1682 m_size += other.m_size;
1683 other.m_size = 0;
1684 }
1685 else
1686 {
1687 swap(other);
1688 }
1689 }
1690
1691 template<typename T>
1693 {
1694 transfer(pos, std::move(other), other.before_begin(), other.end());
1695 }
1696
1697 template<typename T>
1699 {
1700 transfer(pos, std::move(other), other.before_begin(), other.end());
1701 }
1702
1703 template<typename T>
1705 {
1706 transfer(pos, std::move(other), iter);
1707 }
1708
1709 template<typename T>
1711 {
1712 transfer(pos, std::move(other), iter);
1713 }
1714
1715 template<typename T>
1717 const const_iterator& first, const const_iterator& last)
1718 {
1719 transfer(pos, std::move(other), first, last);
1720 }
1721
1722 template<typename T>
1724 {
1725 transfer(pos, std::move(other), first, last);
1726 }
1727
1728 template<typename T>
1730 {
1731 return remove_if([value](T node_val) { return node_val == value; });
1732 }
1733
1734 template<typename T>
1735 template<typename UnaryPred>
1736 auto ForwardList<T>::remove_if(UnaryPred predicate) -> size_type
1737 {
1738 NodeBase* temp{ m_head };
1739 NodeBase* next{};
1740
1741 size_type removed_count{};
1742
1743 while (temp)
1744 {
1745 next = temp->m_next;
1746
1747 if (Node* node = dynamic_cast<Node*>(next))
1748 {
1749 if (predicate(node->value()))
1750 {
1751 NodeBase* to_remove = temp->m_next;
1752 temp->m_next = to_remove->m_next;
1753 destroy_node(to_remove);
1754
1755 removed_count++;
1756 m_size--;
1757 continue;
1758 }
1759 }
1760
1761 temp = temp->m_next;
1762 }
1763
1764 return removed_count;
1765 }
1766
1767 template<typename T>
1769 {
1770 NodeBase* temp{ m_head->m_next };
1771
1772 auto iter = find_iter_before_last();
1773 NodeBase* last{ iter.m_current_node->m_next };
1774
1775 m_head->m_next = last;
1776 last = temp;
1777
1778 NodeBase* prev{};
1779 NodeBase* next{};
1780
1781 for (size_type i = 0; i < m_size; i++)
1782 {
1783 next = temp->m_next;
1784 temp->m_next = prev;
1785
1786 prev = temp;
1787 temp = next;
1788 }
1789
1790 m_head->m_next = std::move(prev);
1791 }
1792
1793 template<typename T>
1795 {
1796 return unique(std::equal_to<>());
1797 }
1798
1799 template<typename T>
1800 template<typename BinaryPred>
1801 auto ForwardList<T>::unique(BinaryPred predicate) -> size_type
1802 {
1803 NodeBase* temp{ m_head };
1804 NodeBase* prev{};
1805 NodeBase* next{};
1806
1807 size_type removed_count{};
1808
1809 while (temp)
1810 {
1811 prev = temp;
1812
1813 while (prev)
1814 {
1815 next = prev->m_next;
1816
1817 Node* node_next = dynamic_cast<Node*>(next);
1818 Node* node_temp = dynamic_cast<Node*>(temp);
1819 if (node_next && node_temp)
1820 {
1821 if (predicate(node_next->value(), node_temp->value()))
1822 {
1823 NodeBase* to_remove = next;
1824 prev->m_next = to_remove->m_next;
1825 destroy_node(to_remove);
1826
1827 removed_count++;
1828 m_size--;
1829 continue;
1830 }
1831 }
1832
1833 prev = prev->m_next;
1834 temp = temp->m_next;
1835 }
1836 }
1837
1838 return removed_count;
1839 }
1840
1841 template<typename T>
1843 {
1844 m_head->m_next = merge_sort(m_head->m_next, std::less<>());
1845 }
1846
1847 template<typename T>
1848 template<typename Compare>
1849 void ForwardList<T>::sort(Compare comp)
1850 {
1851 m_head->m_next = merge_sort(m_head->m_next, comp);
1852 }
1853
1862 template<typename T>
1863 auto operator<<(std::ostream& out, const ForwardList<T>& list) -> std::ostream&
1864 {
1865 if (list.empty())
1866 {
1867 return out;
1868 }
1869
1870 for (auto it = list.cbegin(); it != list.cend(); ++it)
1871 {
1872 T value = *it;
1873 out << value << ' ';
1874 }
1875
1876 return out;
1877 }
1878
1888 template<typename T>
1889 [[nodiscard]] auto operator==(const ForwardList<T>& lhs, const ForwardList<T>& rhs)
1890 noexcept(noexcept(*lhs.begin() == *rhs.begin())) -> bool
1891 {
1892 if (lhs.size() != rhs.size())
1893 {
1894 return false;
1895 }
1896
1897 auto lhs_iter = lhs.cbegin();
1898 auto rhs_iter = rhs.cbegin();
1899
1900 while (lhs_iter != lhs.cend())
1901 {
1902 if (*lhs_iter != *rhs_iter)
1903 {
1904 return false;
1905 }
1906
1907 lhs_iter++;
1908 rhs_iter++;
1909 }
1910
1911 return true;
1912 }
1913
1927 template<typename T>
1928 [[nodiscard]] auto operator<=>(const ForwardList<T>& lhs, const ForwardList<T>& rhs)
1929 noexcept(noexcept(*lhs.begin() == *rhs.begin())) -> std::compare_three_way_result_t<T>
1930 {
1931 auto lhs_iter = lhs.cbegin();
1932 auto rhs_iter = rhs.cbegin();
1933
1934 while (lhs_iter != lhs.cend() && rhs_iter != rhs.cend())
1935 {
1936 auto cmp = *lhs_iter <=> *rhs_iter;
1937 if (cmp != 0)
1938 {
1939 return cmp;
1940 }
1941
1942 lhs_iter++;
1943 rhs_iter++;
1944 }
1945
1946 // first n elements are equal
1947 // check sizes
1948 return lhs.size() <=> rhs.size();
1949 }
1950
1958 template<typename T>
1959 void swap(ForwardList<T>& lhs, ForwardList<T>& rhs) noexcept(noexcept(lhs.swap(rhs)))
1960 {
1961 lhs.swap(rhs);
1962 }
1963
1973 template<typename T, typename U>
1974 auto erase(ForwardList<T>& container, const U& value) -> ForwardList<T>::size_type
1975 {
1976 return erase_if(container, [&value](U node_val) { return node_val == value; });
1977 }
1978
1988 template<typename T, typename Pred>
1990 {
1991 return container.remove_if(pred);
1992 }
1993
1994 // definitions of private methods
1995
1996 template<typename T>
1997 void ForwardList<T>::init_node()
1998 {
1999 if (m_head == nullptr)
2000 {
2001 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
2002 m_head = new NodeBase;
2003 }
2004 }
2005
2006 template<typename T>
2007 auto ForwardList<T>::find_iter_before_last() -> iterator
2008 {
2009 NodeBase* temp{ m_head->m_next };
2010 while (temp && temp->m_next && temp->m_next->m_next)
2011 {
2012 temp = temp->m_next;
2013 }
2014
2015 auto iter = iterator(temp);
2016 if (!iter.m_current_node)
2017 {
2018 iter = before_begin();
2019 }
2020
2021 return iter;
2022 }
2023
2024 template<typename T>
2025 auto ForwardList<T>::erase_element_after(iterator pos) -> iterator
2026 {
2027 NodeBase* temp{ pos.m_current_node };
2028 NodeBase* to_remove{ temp->m_next };
2029
2030 temp->m_next = to_remove->m_next;
2031 destroy_node(to_remove);
2032
2033 m_size--;
2034 return iterator(temp->m_next);
2035 }
2036
2037 template<typename T>
2038 template<typename... Args>
2039 auto ForwardList<T>::construct_node(NodeBase* next_ptr, Args&&... args) -> Node*
2040 {
2041 Node* newNode{};
2042 try
2043 {
2044 newNode = node_alloc_traits::allocate(node_alloc, 1);
2045 node_alloc_traits::construct(node_alloc, newNode, std::forward<Args>(args)...);
2046 newNode->m_next = next_ptr;
2047 }
2048 catch (...)
2049 {
2050 node_alloc_traits::deallocate(node_alloc, newNode, 1);
2051 throw;
2052 }
2053
2054 return newNode;
2055 }
2056
2057 template<typename T>
2058 void ForwardList<T>::destroy_node(NodeBase* node_to_destroy)
2059 {
2060 if (Node* node_dyn = dynamic_cast<Node*>(node_to_destroy))
2061 {
2062 node_alloc_traits::destroy(node_alloc, node_dyn);
2063 node_alloc_traits::deallocate(node_alloc, node_dyn, 1);
2064 }
2065 }
2066
2067 template<typename T>
2068 auto ForwardList<T>::if_valid_iterator(const const_iterator& pos) -> bool
2069 {
2070 bool valid_iterator{};
2071 for (auto it = cbefore_begin(); it != cend(); ++it)
2072 {
2073 if (it == pos)
2074 {
2075 valid_iterator = true;
2076 break;
2077 }
2078 }
2079 return valid_iterator;
2080 }
2081
2082 template<typename T>
2083 auto ForwardList<T>::distance(const_iterator first, const const_iterator& last) -> size_type
2084 {
2085 size_type dist{};
2086 while (first != last)
2087 {
2088 ++first;
2089 ++dist;
2090 }
2091
2092 return dist;
2093 }
2094
2095 template<typename T>
2096 // transfers ownership of nodes, moving entire container is not necessary
2097 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
2098 void ForwardList<T>::transfer(const const_iterator& pos, ForwardList<T>&& other, const const_iterator& iter)
2099 {
2100 if (&other != this && other.m_size > 0)
2101 {
2102 NodeBase* temp_prev{ pos.m_current_node }; // to append to
2103 NodeBase* temp_next{ iter.m_current_node }; // does not move
2104
2105 NodeBase* to_move{ temp_next->m_next };
2106
2107 temp_next->m_next = to_move->m_next;
2108 to_move->m_next = temp_prev->m_next;
2109 temp_prev->m_next = to_move;
2110
2111 m_size += 1;
2112 other.m_size -= 1;
2113 }
2114 }
2115
2116 template<typename T>
2117 // transfers ownership of nodes, moving entire container is not necessary
2118 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
2119 void ForwardList<T>::transfer(const const_iterator& pos, ForwardList<T>&& other,
2120 const const_iterator& first, const const_iterator& last)
2121 {
2122 if (&other != this && other.m_size > 0)
2123 {
2124 const size_type dist = distance(first, last) - 1;
2125
2126 if (first == last || dist == 0)
2127 {
2128 return;
2129 }
2130
2131 NodeBase* temp_prev{ pos.m_current_node }; // to append to
2132 NodeBase* temp_next{ first.m_current_node }; // does not move
2133
2134 NodeBase* first_to_move{ temp_next->m_next };
2135 NodeBase* last_to_move{ temp_next };
2136 for (size_type i = 0; i < dist; i++)
2137 {
2138 last_to_move = last_to_move->m_next;
2139 }
2140
2141 temp_next->m_next = last_to_move->m_next;
2142 last_to_move->m_next = temp_prev->m_next;
2143 temp_prev->m_next = first_to_move;
2144
2145 m_size += dist;
2146 other.m_size -= dist;
2147 }
2148 }
2149
2150 template<typename T>
2151 template<typename Compare>
2152 // Intentional recursive call for sorting nodes in top-down algorithm implementation
2153 // NOLINTNEXTLINE(misc-no-recursion)
2154 auto ForwardList<T>::merge_sort(NodeBase* source, Compare comp) -> NodeBase*
2155 {
2156 // Stop condition, one element list is already sorted
2157 if (source == nullptr || source->m_next == nullptr)
2158 {
2159 return source;
2160 }
2161
2162 // Divide list into equal-sized sublists consisting of first and second half of the list
2163 NodeBase* left{ source };
2164 NodeBase* right{};
2165
2166 // Use slow and fast pointer to find half of list
2167 NodeBase* slow{ source };
2168 NodeBase* fast{ source->m_next };
2169 while (fast && fast->m_next)
2170 {
2171 slow = slow->m_next;
2172 fast = fast->m_next->m_next;
2173 }
2174
2175 // Split input list into two halfs
2176 right = slow->m_next;
2177 slow->m_next = nullptr;
2178
2179 // Recursively sort both sub-lists
2180 left = merge_sort(left, comp);
2181 right = merge_sort(right, comp);
2182
2183 // Merge sorted sublists
2184 NodeBase* result{ merge(left, right, comp) };
2185 return result;
2186 }
2187
2188 template<typename T>
2189 template<typename Compare>
2190 // Intentional recursive call for merging nodes in top-down merge_sort algorithm implementation
2191 // NOLINTNEXTLINE(misc-no-recursion)
2192 auto ForwardList<T>::merge(NodeBase* left, NodeBase* right, Compare comp) -> NodeBase*
2193 {
2194 // Stop condition, empty element list is already sorted
2195 if (left == nullptr)
2196 {
2197 return right;
2198 }
2199 if (right == nullptr)
2200 {
2201 return left;
2202 }
2203
2204 NodeBase* result{};
2205 Node* node_left = dynamic_cast<Node*>(left);
2206 Node* node_right = dynamic_cast<Node*>(right);
2207 if (node_left && node_right)
2208 {
2209 // Recursively merge nodes
2210 if (comp(node_left->m_value, node_right->m_value))
2211 {
2212 result = left;
2213 result->m_next = merge(left->m_next, right, comp);
2214 }
2215 else
2216 {
2217 result = right;
2218 result->m_next = merge(left, right->m_next, comp);
2219 }
2220 }
2221
2222 return result;
2223 }
2224}
2225
2226#endif // !FORWARD_LIST_H
constexpr auto operator==(const Array< T, N > &lhs, const Array< T, N > &rhs) noexcept(noexcept(*lhs.begin()== *rhs.begin())) -> bool
The relational operator compares two Array objects.
Definition array.h:591
constexpr auto operator<=>(const Array< T, N > &lhs, const Array< T, N > &rhs) noexcept(noexcept(*lhs.begin()== *rhs.begin())) -> std::compare_three_way_result_t< T >
The relational operator compares two Array objects.
Definition array.h:625
void swap(Array< T, N > &lhs, Array< T, N > &rhs) noexcept(noexcept(lhs.swap(rhs)))
Exchanges content of two Array containers.
Definition array.h:516
auto operator<<(std::ostream &out, const Array< T, N > &array) -> std::ostream &
Overloads operator to print all elements of Array.
Definition array.h:572
Implements ForwardListIterator.
auto operator!=(const ForwardListIterator< IF_CONST > &other) const -> bool
Overload operator!= for ForwardListIterator objects comparison.
auto operator++(int) -> ForwardListIterator
Overload post-increment operator++ to point ForwardListIterator at next Node.
std::ptrdiff_t difference_type
Alias for pointer difference type.
auto operator->() -> pointer
Overload operator-> to get pointer to Node value / data.
T value_type
Alias for data type used by iterator.
auto operator*() const -> reference
Overload operator* to dereference Node value / data.
iterator_type * pointer
Alias for pointer to data type used by iterator.
auto operator==(const ForwardListIterator< IF_CONST > &other) const -> bool
Overload operator!= for ForwardListIterator objects comparison.
ForwardListIterator() noexcept=default
Construct a new ForwardListIterator object.
std::conditional_t< IF_CONST, const T, T > iterator_type
Alias for iterator type.
iterator_type & reference
Alias for reference to type used by iterator.
auto operator++() -> ForwardListIterator &
Overload pre-increment operator++ to point ForwardListIterator at next Node.
std::forward_iterator_tag iterator_category
Alias for forward iterator tag, define iterator direction.
std::size_t size_type
Alias for size type used in class.
auto operator=(NodeBase *node) -> ForwardListIterator &
Overload operator= to assign node to currently pointed ForwardListIterator.
Struct implements base pointer used by ForwardList.
auto operator=(NodeBase &&other) -> NodeBase &=default
Construct NodeBase object using move assignment.
NodeBase()=default
Construct a new NodeBase object.
NodeBase(NodeBase &&other)=default
Construct NodeBase object using move constructor.
auto operator=(const NodeBase &other) -> NodeBase &=default
Construct a new NodeBase object using copy assignment.
virtual ~NodeBase()=default
Destroy the Node Base object.
NodeBase(const NodeBase &other)=default
Construct a new NodeBase object using copy constructor.
Implements Node class with user data.
auto value() const -> T
Function returns value stored in Node object.
auto value() -> T &
Function returns value stored in Node object.
Node(T &&value)
Construct a new Node object with initial value using move semantics.
Node(const T &value)
Construct a new Node object with initial value.
friend class ForwardList
Forward friend declaration of ForwardList.
Implements ForwardList using Node with pointer to next element as internal base.
void clear()
Function removes all elements of ForwardList.
T value_type
Alias for data type used in class.
value_type & reference
Alias for reference to data type used in class.
auto emplace_front(Args &&... args) -> reference
Inserts a new element to the beginning of the container.
auto max_size() const noexcept -> size_type
Function returns maximum number of elements container can hold.
~ForwardList()
Destroy the ForwardList object.
auto emplace_after(const_iterator pos, Args &&... args) -> iterator
Insert new element into the container after pos.
auto operator=(const ForwardList< T > &other) -> ForwardList &
Constructs ForwardList using copy assignment.
std::allocator< value_type > allocator_type
Alias for memory allocator.
void splice_after(const const_iterator &pos, ForwardList< T > &other)
Function moves elements from other ForwardList object.
auto cend() const noexcept -> const_iterator
Function returns pointer to ForwardList last Node.
const value_type * const_pointer
Alias for const pointer to data type used in class.
ForwardListIterator< true > const_iterator
Alias for const iterator to data type used in class.
auto begin() noexcept -> iterator
Function returns pointer to ForwardList first Node.
void reverse()
Function reverts in place Nodes of ForwardList.
void assign(size_type count, const_reference value)
Function assign values to the ForwardList.
auto before_begin() noexcept -> iterator
Function returns iterator just before ForwardList first Node.
ForwardList()
Construct a new ForwardList object.
void swap(ForwardList< T > &other) noexcept(std::is_nothrow_swappable_v< T >)
Function swaps content of two ForwardList objects.
auto front() -> reference
Function returns reference to value stored in ForwardList first Node.
void pop_front()
Function removes first Node of ForwardList.
const value_type & const_reference
Alias for const reference to data type used in class.
auto cbegin() const noexcept -> const_iterator
Function returns const pointer to ForwardList first Node.
std::ptrdiff_t difference_type
Alias for pointer difference type used in class.
std::size_t size_type
Alias for size type used in class.
void resize(size_type count)
Function resize ForwardList to specified number of elements.
ForwardListIterator< false > iterator
Alias for iterator to data type used in class.
auto empty() const -> bool
Function checks if container has no elements.
auto remove_if(UnaryPred predicate) -> size_type
Function removes all elements for which predicate returns true.
void push_front(const_reference value)
Function adds new Node at the beginning of ForwardList.
auto erase_after(const const_iterator &pos) -> iterator
Function erases Node after specified ForwardList const_iterator.
constexpr auto get_allocator() const -> allocator_type
Get the allocator object.
auto insert_after(const const_iterator &pos, const_reference value) -> iterator
Function inserts new Node after specified ForwardList iterator.
void merge(ForwardList< T > &other)
Function combines two sorted ForwardLists into one sorted ForwardList.
auto end() noexcept -> iterator
Function returns pointer to ForwardList last Node.
auto remove(const_reference value) -> size_type
Function removes all elements equal to value.
auto size() const -> size_type
Function returns ForwardList size.
value_type * pointer
Alias for pointer to data type used in class.
void sort()
Function sorts the elements and preserves the order of equivalent elements.
auto unique() -> size_type
Function removes consecutive duplicated elements.
auto cbefore_begin() const noexcept -> const_iterator
Function returns const_iterator just before ForwardList first Node.
auto erase(ForwardList< T > &container, const U &value) -> ForwardList< T >::size_type
Function erases from container all elements that are equal to value.
auto erase_if(ForwardList< T > &container, Pred pred) -> ForwardList< T >::size_type
Function erases from container all elements that satisfy the predicate pred.