DSA - Data Structures and Algorithms
Loading...
Searching...
No Matches
vector.h
Go to the documentation of this file.
1
11
12#ifndef VECTOR_H
13#define VECTOR_H
14
15#include <cstddef>
16#include <initializer_list>
17#include <iostream>
18#include <iterator>
19#include <memory>
20
21namespace dsa
22{
23
40 template<typename T>
41 class Vector
42 {
43 public:
44
50 using value_type = T;
51
55 using allocator_type = std::allocator<value_type>;
56
62 using size_type = std::size_t;
63
69 using difference_type = std::ptrdiff_t;
70
76 using pointer = T*;
77
83 using const_pointer = const T*;
84
90 using reference = T&;
91
97 using const_reference = const T&;
98
102 using iterator = T*;
103
107 using const_iterator = const T*;
108
112 using reverse_iterator = std::reverse_iterator<iterator>;
113
117 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
118
122 constexpr Vector();
123
130 constexpr Vector(size_type count);
131
139 constexpr Vector(size_type count, const T& value);
140
148 template<typename InputIt>
149 requires std::input_iterator<InputIt>
150 constexpr Vector(InputIt first, InputIt last);
151
157 constexpr Vector(const Vector<T>& other);
158
165 constexpr Vector(Vector<T>&& other) noexcept;
166
172 constexpr Vector(std::initializer_list<T> init_list);
173
177 ~Vector();
178
186 constexpr auto operator=(const Vector<T>& other) -> Vector<T>&;
187
195 constexpr auto operator=(Vector<T>&& other) noexcept -> Vector<T>&;
196
203 constexpr auto operator=(std::initializer_list<T> init_list) -> Vector<T>&;
204
211 constexpr void assign(size_type count, const T& value);
212
220 template<typename InputIt>
221 requires std::input_iterator<InputIt>
222 // do not use `last` as const reference to keep arguments the same as in std
223 // NOLINTNEXTLINE(performance-unnecessary-value-param)
224 constexpr void assign(InputIt first, InputIt last);
225
231 constexpr void assign(std::initializer_list<T> init_list);
232
238 [[nodiscard]] constexpr auto get_allocator() const -> allocator_type;
239
247 [[nodiscard]] constexpr auto at(size_type pos) -> reference;
248
256 [[nodiscard]] constexpr auto at(size_type pos) const -> const_reference;
257
265 [[nodiscard]] constexpr auto operator[](size_type pos)->reference;
266
274 [[nodiscard]] constexpr auto operator[](size_type pos) const->const_reference;
275
281 [[nodiscard]] constexpr auto front() -> reference;
282
288 [[nodiscard]] constexpr auto front() const -> const_reference;
289
295 [[nodiscard]] constexpr auto back() -> reference;
296
302 [[nodiscard]] constexpr auto back() const -> const_reference;
303
309 [[nodiscard]] constexpr auto data() noexcept -> pointer;
310
316 [[nodiscard]] constexpr auto data() const noexcept -> const_pointer;
317
325 [[nodiscard]] constexpr auto begin() noexcept -> iterator;
326
334 [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator;
335
343 [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator;
344
352 [[nodiscard]] constexpr auto end() noexcept -> iterator;
353
361 [[nodiscard]] constexpr auto end() const noexcept -> const_iterator;
362
370 [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator;
371
379 [[nodiscard]] constexpr auto rbegin() -> reverse_iterator;
380
388 [[nodiscard]] constexpr auto rbegin() const -> const_reverse_iterator;
389
397 [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator;
398
406 [[nodiscard]] constexpr auto rend() -> reverse_iterator;
407
415 [[nodiscard]] constexpr auto rend() const -> const_reverse_iterator;
416
424 [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator;
425
432 [[nodiscard]] constexpr auto empty() const -> bool;
433
439 [[nodiscard]] constexpr auto size() const noexcept -> size_type;
440
446 [[nodiscard]] constexpr auto max_size() const noexcept -> size_type;
447
453 constexpr void reserve(size_type new_cap);
454
460 constexpr auto capacity() -> size_type;
461
468 constexpr void shrink_to_fit();
469
476 constexpr void clear();
477
485 constexpr auto insert(const_iterator pos, const T& value) -> iterator;
486
494 constexpr auto insert(const_iterator pos, T&& value) -> iterator;
495
507 constexpr auto insert(const_iterator pos, size_type count, const T& value) -> iterator;
508
521 template<typename InputIt>
522 requires std::input_iterator<InputIt>
523 constexpr auto insert(const_iterator pos, InputIt first, InputIt last) -> iterator;
524
535 constexpr auto insert(const_iterator pos, std::initializer_list<T> init_list) -> iterator;
536
548 template<typename... Args>
549 constexpr auto emplace(const_iterator pos, Args&&... args) -> iterator;
550
558 template<typename... Args>
559 constexpr auto emplace_back(Args&&... args) -> reference;
560
570 constexpr auto erase(iterator pos) -> iterator;
571
581 constexpr auto erase(const_iterator pos) -> iterator;
582
593 constexpr auto erase(iterator first, iterator last) -> iterator;
594
605 constexpr auto erase(const_iterator first, const_iterator last) -> iterator;
606
612 constexpr void push_back(const T& value);
613
619 constexpr void push_back(T&& value);
620
626 constexpr void pop_back();
627
636 constexpr void resize(size_type count);
637
647 constexpr void resize(size_type count, const value_type& value);
648
654 constexpr void swap(Vector<T>& other) noexcept;
655
656 private:
657
663 auto calc_new_capacity() -> size_type;
664
673 auto insert_make_space_for_new_elems(const_iterator pos, size_type count) -> iterator;
674
682 void reallocate(size_type new_cap);
683
687 void destroy_elements();
688
692 void clear_allocation();
693
697 value_type* m_data{};
698
702 size_type m_size{};
703
707 size_type m_capacity{};
708
712 allocator_type m_allocator{};
713 };
714
715 template<typename T>
716 constexpr Vector<T>::Vector() = default;
717
718 template<typename T>
720 : Vector(count, T())
721 {
722 }
723
724 template<typename T>
725 constexpr Vector<T>::Vector(size_type count, const T& value)
726 {
727 for (size_t i = 0; i < count; i++)
728 {
729 push_back(value);
730 }
731 }
732
733 template<typename T>
734 template<typename InputIt>
735 requires std::input_iterator<InputIt>
736 constexpr Vector<T>::Vector(InputIt first, InputIt last)
737 {
738 // do not use const reference to keep arguments the same as in std
739 // NOLINTNEXTLINE(performance-unnecessary-value-param)
740 assign(first, last);
741 }
742
743 template<typename T>
744 constexpr Vector<T>::Vector(const Vector<T>& other)
745 {
746 reserve(other.size());
747 for (const auto& item : other)
748 {
749 push_back(item);
750 }
751 }
752
753 template<typename T>
754 constexpr Vector<T>::Vector(Vector<T>&& other) noexcept
755 {
756 operator=(std::move(other));
757 }
758
759 template<typename T>
760 constexpr Vector<T>::Vector(std::initializer_list<T> init_list)
761 {
762 assign(init_list);
763 }
764
765 template<typename T>
767 {
768 clear_allocation();
769 }
770
771 template<typename T>
772 constexpr auto Vector<T>::operator=(const Vector<T>& other) -> Vector<T>&
773 {
774 if (&other != this)
775 {
776 clear_allocation();
777 m_capacity = 0;
778 m_size = 0;
779
780 m_data = allocator_type().allocate(other.size());
781 for (const auto& item : other)
782 {
783 push_back(item);
784 }
785 }
786
787 return *this;
788 }
789
790 template<typename T>
791 constexpr auto Vector<T>::operator=(Vector<T>&& other) noexcept -> Vector<T>&
792 {
793 if (&other != this)
794 {
795 clear_allocation();
796
797 m_data = other.m_data;
798 m_capacity = other.m_capacity;
799 m_size = other.m_size;
800
801 other.m_data = nullptr;
802 other.m_capacity = 0;
803 other.m_size = 0;
804 }
805
806 return *this;
807 }
808
809 template<typename T>
810 constexpr auto Vector<T>::operator=(std::initializer_list<T> init_list) -> Vector<T>&
811 {
812 clear_allocation();
813 m_capacity = 0;
814 m_size = 0;
815
816 m_data = allocator_type().allocate(init_list.size());
817 for (auto const& item : init_list)
818 {
819 push_back(item);
820 }
821
822 return *this;
823 }
824
825 template<typename T>
826 constexpr void Vector<T>::assign(size_type count, const T& value)
827 {
828 clear_allocation();
829 m_capacity = 0;
830 m_size = 0;
831
832 m_data = allocator_type().allocate(count);
833 for (size_t i = 0; i < count; i++)
834 {
835 push_back(value);
836 }
837 }
838
839 template<typename T>
840 template<typename InputIt>
841 requires std::input_iterator<InputIt>
842 // do not use `last` as const reference to keep arguments the same as in std
843 // NOLINTNEXTLINE(performance-unnecessary-value-param)
844 constexpr void Vector<T>::assign(InputIt first, InputIt last)
845 {
846 const auto count = static_cast<size_type>(std::distance(first, last));
847
848 clear_allocation();
849 m_capacity = 0;
850 m_size = 0;
851
852 m_data = allocator_type().allocate(count);
853 while (first != last)
854 {
855 push_back(*first);
856 first++;
857 }
858 }
859
860 template<typename T>
861 constexpr void Vector<T>::assign(std::initializer_list<T> init_list)
862 {
863 clear_allocation();
864 m_capacity = 0;
865 m_size = 0;
866
867 m_data = allocator_type().allocate(init_list.size());
868 for (auto const& item : init_list)
869 {
870 push_back(item);
871 }
872 }
873
874 template<typename T>
875 [[nodiscard]] constexpr auto Vector<T>::get_allocator() const -> allocator_type
876 {
877 return m_allocator;
878 }
879
880 template<typename T>
881 [[nodiscard]] constexpr auto Vector<T>::at(size_type pos) -> reference
882 {
883 if (pos >= m_size)
884 {
885 throw std::out_of_range("Pos argument outside of container range");
886 }
887
888 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
889 return m_data[pos];
890 }
891
892 template<typename T>
893 [[nodiscard]] constexpr auto Vector<T>::at(size_type pos) const -> const_reference
894 {
895 if (pos >= m_size)
896 {
897 throw std::out_of_range("Pos argument outside of container range");
898 }
899
900 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
901 return m_data[pos];
902 }
903
904 template<typename T>
905 [[nodiscard]] constexpr auto Vector<T>::operator[](size_type pos) -> reference
906 {
907 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
908 return m_data[pos];
909 }
910
911 template<typename T>
912 [[nodiscard]] constexpr auto Vector<T>::operator[](size_type pos) const -> const_reference
913 {
914 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
915 return m_data[pos];
916 }
917
918 template<typename T>
919 [[nodiscard]] constexpr auto Vector<T>::front() -> reference
920 {
921 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
922 return m_data[0];
923 }
924
925 template<typename T>
926 [[nodiscard]] constexpr auto Vector<T>::front() const -> const_reference
927 {
928 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
929 return m_data[0];
930 }
931
932 template<typename T>
933 [[nodiscard]] constexpr auto Vector<T>::back() -> reference
934 {
935 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
936 return m_data[m_size - 1];
937 }
938
939 template<typename T>
940 [[nodiscard]] constexpr auto Vector<T>::back() const -> const_reference
941 {
942 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
943 return m_data[m_size - 1];
944 }
945
946 template<typename T>
947 [[nodiscard]] constexpr auto Vector<T>::data() noexcept -> pointer
948 {
949 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
950 return &m_data[0];
951 }
952
953 template<typename T>
954 [[nodiscard]] constexpr auto Vector<T>::data() const noexcept -> const_pointer
955 {
956 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
957 return &m_data[0];
958 }
959
960 template<typename T>
961 [[nodiscard]] constexpr auto Vector<T>::begin() noexcept -> iterator
962 {
963 return m_data;
964 }
965
966 template<typename T>
967 [[nodiscard]] constexpr auto Vector<T>::begin() const noexcept -> const_iterator
968 {
969 return m_data;
970 }
971
972 template<typename T>
973 [[nodiscard]] constexpr auto Vector<T>::cbegin() const noexcept -> const_iterator
974 {
975 return m_data;
976 }
977
978 template<typename T>
979 [[nodiscard]] constexpr auto Vector<T>::end() noexcept -> iterator
980 {
981 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
982 return m_data + m_size;
983 }
984
985 template<typename T>
986 [[nodiscard]] constexpr auto Vector<T>::end() const noexcept -> const_iterator
987 {
988 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
989 return m_data + m_size;
990 }
991
992 template<typename T>
993 [[nodiscard]] constexpr auto Vector<T>::cend() const noexcept -> const_iterator
994 {
995 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
996 return m_data + m_size;
997 }
998
999 template<typename T>
1000 [[nodiscard]] constexpr auto Vector<T>::rbegin() -> reverse_iterator
1001 {
1002 return reverse_iterator(end());
1003 }
1004
1005 template<typename T>
1006 [[nodiscard]] constexpr auto Vector<T>::rbegin() const -> const_reverse_iterator
1007 {
1008 return const_reverse_iterator(end());
1009 }
1010
1011 template<typename T>
1012 [[nodiscard]] constexpr auto Vector<T>::crbegin() const noexcept -> const_reverse_iterator
1013 {
1014 return const_reverse_iterator(end());
1015 }
1016
1017 template<typename T>
1018 [[nodiscard]] constexpr auto Vector<T>::rend() -> reverse_iterator
1019 {
1020 return reverse_iterator(begin());
1021 }
1022
1023 template<typename T>
1024 [[nodiscard]] constexpr auto Vector<T>::rend() const -> const_reverse_iterator
1025 {
1026 return const_reverse_iterator(begin());
1027 }
1028
1029 template<typename T>
1030 [[nodiscard]] constexpr auto Vector<T>::crend() const noexcept -> const_reverse_iterator
1031 {
1032 return const_reverse_iterator(begin());
1033 }
1034
1035 template<typename T>
1036 [[nodiscard]] constexpr auto Vector<T>::empty() const -> bool
1037 {
1038 return m_size == 0;
1039 }
1040
1041 template<typename T>
1042 [[nodiscard]] constexpr auto Vector<T>::size() const noexcept -> size_type
1043 {
1044 return m_size;
1045 }
1046
1047 template<typename T>
1048 [[nodiscard]] constexpr auto Vector<T>::max_size() const noexcept -> size_type
1049 {
1050 return std::allocator_traits<allocator_type>::max_size(get_allocator());
1051 }
1052
1053 template<typename T>
1054 constexpr void Vector<T>::reserve(size_type new_cap)
1055 {
1056 if (new_cap > capacity())
1057 {
1058 // strong exception guarantee by reallocate
1059 reallocate(new_cap);
1060 }
1061 }
1062
1063 template<typename T>
1064 constexpr auto Vector<T>::capacity() -> size_type
1065 {
1066 return m_capacity;
1067 }
1068
1069 template<typename T>
1071 {
1072 if (m_capacity > m_size)
1073 {
1074 reallocate(m_size);
1075 }
1076 }
1077
1078 template<typename T>
1079 constexpr void Vector<T>::clear()
1080 {
1081 destroy_elements();
1082 m_size = 0;
1083 }
1084
1085 template<typename T>
1086 constexpr auto Vector<T>::insert(const_iterator pos, const T& value) -> iterator
1087 {
1088 return insert(pos, 1, value);
1089 }
1090
1091 template<typename T>
1092 constexpr auto Vector<T>::insert(const_iterator pos, T&& value) -> iterator
1093 {
1094 return insert(pos, 1, std::move(value));
1095 }
1096
1097 template<typename T>
1098 constexpr auto Vector<T>::insert(const_iterator pos, size_type count, const T& value) -> iterator
1099 {
1100 iterator new_pos{ insert_make_space_for_new_elems(pos, count) };
1101
1102 // create objects in range [pos, pos + count]
1103 iterator new_iter{ new_pos };
1104 for (size_t i = 0; i < count; i++)
1105 {
1106 std::allocator_traits<allocator_type>::construct(m_allocator, new_iter, value);
1107 ++new_iter;
1108 }
1109
1110 return new_pos;
1111 }
1112
1113 template<typename T>
1114 template<typename InputIt>
1115 requires std::input_iterator<InputIt>
1116 constexpr auto Vector<T>::insert(const_iterator pos, InputIt first, InputIt last) -> iterator
1117 {
1118 const auto count = static_cast<size_type>(std::distance(first, last));
1119 iterator new_pos{ insert_make_space_for_new_elems(pos, count) };
1120
1121 // create objects in range [pos, pos + count]
1122 iterator new_iter{ new_pos };
1123 while (first != last)
1124 {
1125 std::allocator_traits<allocator_type>::construct(m_allocator, new_iter, *first);
1126 ++new_iter;
1127 ++first;
1128 }
1129
1130 return new_pos;
1131 }
1132
1133 template<typename T>
1134 constexpr auto Vector<T>::insert(const_iterator pos, std::initializer_list<T> init_list) -> iterator
1135 {
1136 const size_type count = init_list.size();
1137 iterator new_pos{ insert_make_space_for_new_elems(pos, count) };
1138
1139 // create objects in range [pos, pos + count]
1140 iterator new_iter{ new_pos };
1141 for (const auto& value : init_list)
1142 {
1143 std::allocator_traits<allocator_type>::construct(m_allocator, new_iter, value);
1144 ++new_iter;
1145 }
1146
1147 return new_pos;
1148 }
1149
1150 template<typename T>
1151 template<typename... Args>
1152 constexpr auto Vector<T>::emplace(const_iterator pos, Args&&... args) -> iterator
1153 {
1154 iterator new_pos{ insert_make_space_for_new_elems(pos, 1) };
1155
1156 // emplace new element
1157 std::allocator_traits<allocator_type>::construct(m_allocator, new_pos, std::forward<Args>(args)...);
1158
1159 return new_pos;
1160 }
1161
1162 template<typename T>
1163 template<typename... Args>
1164 constexpr auto Vector<T>::emplace_back(Args&&... args) -> reference
1165 {
1166 /*
1167 * If an exception is thrown for any reason, this function has no effect
1168 * strong exception guarantee by:
1169 * - reallocate
1170 * - if construction of new element fails, state of object does not change
1171 */
1172
1173 if (m_size >= m_capacity)
1174 {
1175 reallocate(calc_new_capacity());
1176 }
1177 std::allocator_traits<allocator_type>::construct(m_allocator, end(), std::forward<Args>(args)...);
1178 ++m_size;
1179
1180 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1181 return m_data[m_size - 1];
1182 }
1183
1184 template<typename T>
1185 constexpr auto Vector<T>::erase(iterator pos) -> iterator
1186 {
1187 return erase(pos, pos + 1);
1188 }
1189
1190 template<typename T>
1192 {
1193 return erase(pos, pos + 1);
1194 }
1195
1196 template<typename T>
1197 constexpr auto Vector<T>::erase(iterator first, iterator last) -> iterator
1198 {
1199 return erase(static_cast<const_iterator>(first), static_cast<const_iterator>(last));
1200 }
1201
1202 template<typename T>
1204 {
1205 const auto offset_last = static_cast<size_type>(last - cbegin());
1206 const auto offset_first = static_cast<size_type>(first - cbegin());
1207 const size_type count{ offset_last - offset_first };
1208
1209 // destroy objects in range [first, last)
1210 while (first != last)
1211 {
1212 std::allocator_traits<allocator_type>::destroy(m_allocator, first);
1213 ++first;
1214 }
1215
1216 // move remaining objects into empty space
1217 // this moves memory in range [last, end()] to addres pointed by `first`, that was already released
1218 std::move(begin() + offset_last, end(), begin() + offset_first);
1219
1220 m_size -= count;
1221 return begin() + offset_first;
1222 }
1223
1224 template<typename T>
1225 constexpr void Vector<T>::push_back(const T& value)
1226 {
1227 if (m_size >= m_capacity)
1228 {
1229 reallocate(calc_new_capacity());
1230 }
1231
1232 std::allocator_traits<allocator_type>::construct(m_allocator, end(), value);
1233 ++m_size;
1234 }
1235
1236 template<typename T>
1237 constexpr void Vector<T>::push_back(T&& value)
1238 {
1239 // Some implementations throw std::lengt_error when push_back causes a reallocation that exceeds mas_size()
1240 if (m_size >= m_capacity)
1241 {
1242 reallocate(calc_new_capacity());
1243 }
1244
1245 std::allocator_traits<allocator_type>::construct(m_allocator, end(), std::move(value));
1246 ++m_size;
1247 }
1248
1249 template<typename T>
1250 constexpr void Vector<T>::pop_back()
1251 {
1252 if (m_size > 0)
1253 {
1254 std::allocator_traits<allocator_type>::destroy(m_allocator, end());
1255 --m_size;
1256 }
1257 }
1258
1259 template<typename T>
1260 constexpr void Vector<T>::resize(size_type count)
1261 {
1262 resize(count, T{});
1263 }
1264
1265 template<typename T>
1266 constexpr void Vector<T>::resize(size_type count, const value_type& value)
1267 {
1268 if (count >= max_size())
1269 {
1270 throw std::length_error("Capacity required by new vector would exceed maximum allowed size");
1271 }
1272
1273 if (count == m_size)
1274 {
1275 return;
1276 }
1277
1278 if (m_size > count)
1279 {
1280 // container is reduced to its count elements
1281 while (m_size > count)
1282 {
1283 pop_back();
1284 }
1285 }
1286
1287 if (m_size < count)
1288 {
1289 // additional copies of value are appended
1290 while (m_size < count)
1291 {
1292 push_back(value);
1293 }
1294 }
1295 }
1296
1297 template<typename T>
1298 constexpr void Vector<T>::swap(Vector<T>& other) noexcept
1299 {
1300 std::swap(*this, other);
1301 }
1302
1303 template<typename T>
1304 inline auto Vector<T>::calc_new_capacity() -> size_type
1305 {
1306 return m_capacity == 0 ? 1 : m_capacity * 2;
1307 }
1308
1309 template<typename T>
1310 auto Vector<T>::insert_make_space_for_new_elems(const_iterator pos, size_type count) -> iterator
1311 {
1312 iterator new_pos{};
1313
1314 if (size() + count > capacity())
1315 {
1316 // allocate memory to store new content
1317 const size_type new_capacity = m_capacity + count;
1318 pointer new_data = allocator_type().allocate(new_capacity);
1319
1320 // use iterator to move all objects from original vector to new allocated memory
1321 iterator iter{ begin() };
1322 iterator new_iter = new_data;
1323
1324 // move all objects before pos from original memory
1325 while (iter != pos)
1326 {
1327 std::allocator_traits<allocator_type>::construct(m_allocator, new_iter, std::move(*iter));
1328 ++iter;
1329 ++new_iter;
1330 }
1331
1332 // make space for new elements
1333 new_pos = new_iter;
1334 for (size_t i = 0; i < count; i++)
1335 {
1336 ++new_iter;
1337 }
1338
1339 // move all remaining objects from original memory
1340 while (iter != end())
1341 {
1342 std::allocator_traits<allocator_type>::construct(m_allocator, new_iter, std::move(*iter));
1343 ++iter;
1344 ++new_iter;
1345 }
1346
1347 clear_allocation();
1348 m_data = new_data;
1349 m_capacity = new_capacity;
1350 m_size += count;
1351 }
1352 else
1353 {
1354 // store address to insert new elements
1355 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
1356 iterator iter{ const_cast<iterator>(pos) };
1357 new_pos = iter;
1358
1359 // move object to the right
1360 std::move(iter, end(), iter + count);
1361
1362 m_size += count;
1363 }
1364
1365 return new_pos;
1366 }
1367
1368 template<typename T>
1369 void Vector<T>::reallocate(size_type new_cap)
1370 {
1371 /*
1372 * strong exception guarantee by:
1373 * - throwing exception in case container is too large
1374 * - using std::move_if_noexcept during reallocation
1375 */
1376
1377 if (new_cap > max_size())
1378 {
1379 throw std::length_error("Pos argument outside of container range");
1380 }
1381
1382 pointer new_data = allocator_type().allocate(new_cap);
1383 for (size_t i = 0; i < m_size; i++)
1384 {
1385 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1386 std::allocator_traits<allocator_type>::construct(m_allocator, new_data + i, std::move_if_noexcept(m_data[i]));
1387 }
1388 clear_allocation();
1389 m_data = new_data;
1390 m_capacity = new_cap;
1391 // m_size stay the same as before reallocation
1392 }
1393
1394 template<typename T>
1395 void Vector<T>::destroy_elements()
1396 {
1397 iterator iter{ begin() };
1398 while (iter != end())
1399 {
1400 std::allocator_traits<allocator_type>::destroy(m_allocator, iter);
1401 ++iter;
1402 }
1403 }
1404
1405 template<typename T>
1406 void Vector<T>::clear_allocation()
1407 {
1408 if (m_data)
1409 {
1410 destroy_elements();
1411 std::allocator_traits<allocator_type>::deallocate(m_allocator, m_data, m_capacity);
1412 m_capacity = 0;
1413 }
1414 }
1415
1423 template<typename T>
1424 void swap(Vector<T>& vector1, Vector<T>& vector2) noexcept
1425 {
1426 vector1.swap(vector2);
1427 }
1428
1437 template<typename T>
1438 auto operator<<(std::ostream& out, const Vector<T>& vector) -> std::ostream&
1439 {
1440 for (size_t i = 0; i < vector.size(); i++)
1441 {
1442 out << vector[i] << ' ';
1443 }
1444
1445 return out;
1446 }
1447
1457 template<typename T>
1458 auto operator==(const Vector<T>& vector1, const Vector<T>& vector2) -> bool
1459 {
1460 if (vector1.size() != vector2.size())
1461 {
1462 return false;
1463 }
1464
1465 auto vector1_iter = vector1.cbegin();
1466 auto vector2_iter = vector2.cbegin();
1467
1468 // vectors have equal size, test condition for one vector
1469 while (vector1_iter != vector1.cend())
1470 {
1471 if (*vector1_iter != *vector2_iter)
1472 {
1473 return false;
1474 }
1475
1476 vector1_iter++;
1477 vector2_iter++;
1478 }
1479
1480 return true;
1481 }
1482
1492 template<typename T>
1493 constexpr auto operator<=>(const Vector<T>& vector1, const Vector<T>& vector2)
1494 {
1495 auto vector1_iter = vector1.cbegin();
1496 auto vector2_iter = vector2.cbegin();
1497
1498 while (vector1_iter != vector1.cend() && vector2_iter != vector2.cend())
1499 {
1500 if (*vector1_iter < *vector2_iter)
1501 {
1502 return std::strong_ordering::less;
1503 }
1504 if (*vector1_iter > *vector2_iter)
1505 {
1506 return std::strong_ordering::greater;
1507 }
1508
1509 vector1_iter++;
1510 vector2_iter++;
1511 }
1512
1513 if (vector1.size() < vector2.size())
1514 {
1515 return std::strong_ordering::less;
1516 }
1517 if (vector1.size() > vector2.size())
1518 {
1519 return std::strong_ordering::greater;
1520 }
1521
1522 return std::strong_ordering::equivalent;
1523 }
1524}
1525
1526#endif // !VECTOR_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 Vector class template for dynamic size container.
Definition vector.h:42
constexpr auto erase(iterator pos) -> iterator
Erases specified element from the container.
Definition vector.h:1185
constexpr auto front() -> reference
Returns reference to first Arary element.
Definition vector.h:919
T value_type
Alias for data type used in class.
Definition vector.h:50
std::reverse_iterator< iterator > reverse_iterator
Alias for reverse_iterator to data type used in class.
Definition vector.h:112
constexpr void clear()
Erases all elements of the container Does not affect container capacity.
Definition vector.h:1079
constexpr auto capacity() -> size_type
Returns the number of elements allocated for container.
Definition vector.h:1064
constexpr auto emplace(const_iterator pos, Args &&... args) -> iterator
Insert new element into the container before pos.
Definition vector.h:1152
constexpr auto crend() const noexcept -> const_reverse_iterator
Returns const_reverse_iterator past the last element of reversed underlaying data structure.
Definition vector.h:1030
constexpr auto crbegin() const noexcept -> const_reverse_iterator
Returns const_reverse_iterator to the first element of reversed underlaying data structure.
Definition vector.h:1012
constexpr auto at(size_type pos) -> reference
Returns a reference to Vector element at pos index. If pos is outside of container range,...
Definition vector.h:881
constexpr auto cbegin() const noexcept -> const_iterator
Returns const_iterator to first element.
Definition vector.h:973
constexpr void shrink_to_fit()
Request to remove of unused capacity.
Definition vector.h:1070
constexpr auto cend() const noexcept -> const_iterator
Returns const_iterator past last element of underlaying data structure.
Definition vector.h:993
constexpr auto max_size() const noexcept -> size_type
Returns maximum number of elements container can hold.
Definition vector.h:1048
std::ptrdiff_t difference_type
Alias for pointer difference type.
Definition vector.h:69
constexpr Vector()
Construct a new Vector object.
const T * const_pointer
Alias for const pointer to data type used in class.
Definition vector.h:83
std::size_t size_type
Alias for size type used in class.
Definition vector.h:62
constexpr auto begin() noexcept -> iterator
Returns iterator to first element.
Definition vector.h:961
constexpr auto get_allocator() const -> allocator_type
Get the allocator object.
Definition vector.h:875
constexpr auto back() -> reference
Returns reference to last Arary element.
Definition vector.h:933
constexpr auto empty() const -> bool
Checks if container has elements.
Definition vector.h:1036
std::reverse_iterator< const_iterator > const_reverse_iterator
Alias for const reverse_iterator to data type used in class.
Definition vector.h:117
constexpr void push_back(const T &value)
Appends a copy of value at the end of the container.
Definition vector.h:1225
const T * const_iterator
Alias for const iterator to data type used in class.
Definition vector.h:107
T & reference
Alias for reference to data type used in class.
Definition vector.h:90
T * pointer
Alias for pointer to data type used in class.
Definition vector.h:76
~Vector()
Destroy the Vector object.
Definition vector.h:766
constexpr void pop_back()
Removes the last element of the container.
Definition vector.h:1250
constexpr auto data() noexcept -> pointer
Returns pointer to underlying data container.
Definition vector.h:947
constexpr void swap(Vector< T > &other) noexcept
Exchanges content of current container with other container.
Definition vector.h:1298
constexpr auto rbegin() -> reverse_iterator
Returns reverse_iterator to the first element of reversed underlaying data structure.
Definition vector.h:1000
constexpr auto size() const noexcept -> size_type
Returns number of elements in container.
Definition vector.h:1042
constexpr auto operator=(const Vector< T > &other) -> Vector< T > &
Assign Vector object using copy assignment.
Definition vector.h:772
constexpr void reserve(size_type new_cap)
Increase the capacity of the container.
Definition vector.h:1054
constexpr auto operator[](size_type pos) -> reference
Returns a reference to Vector element at pos index. If pos is outside of container range,...
Definition vector.h:905
constexpr auto emplace_back(Args &&... args) -> reference
Appends a copy of value at the end of the container.
Definition vector.h:1164
constexpr auto end() noexcept -> iterator
Returns iterator past last element of underlaying data structure.
Definition vector.h:979
std::allocator< value_type > allocator_type
Alias for memory allocator.
Definition vector.h:55
constexpr void resize(size_type count)
Resizez the container to contain count elements if count is equal to current size,...
Definition vector.h:1260
constexpr auto insert(const_iterator pos, const T &value) -> iterator
Insert a copy of value before pos.
Definition vector.h:1086
constexpr void assign(size_type count, const T &value)
Function assign count , elements of value to Vector object.
Definition vector.h:826
T * iterator
Alias for iterator to data type used in class.
Definition vector.h:102
constexpr auto rend() -> reverse_iterator
Returns reverse_iterator past the last element of reversed underlaying data structure.
Definition vector.h:1018
const T & const_reference
Alias for const reference to data type used in class.
Definition vector.h:97