├── .gitignore ├── .clang-format ├── cmake ├── static_vector-config.cmake.in └── install.cmake ├── tests ├── include │ └── initializer_list_helper.hpp ├── CMakeLists.txt ├── 04_iterators.cpp ├── 03_access.cpp ├── 06_modifiers.cpp ├── 05_capacity.cpp ├── 07_non_member_functions.cpp ├── 01_construction.cpp └── 02_assignment.cpp ├── .cmake-format.json ├── .github └── workflows │ └── cpp_unit_tests.yml ├── CMakeLists.txt ├── LICENSE ├── CMakePresets.json ├── README.md └── include └── ecpp └── static_vector.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | CMakeUserPresets.json -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | ColumnLimit: "150" 4 | SpaceBeforeParens: Never 5 | RemoveSemicolon: true 6 | QualifierAlignment: Right 7 | InsertNewlineAtEOF: true 8 | -------------------------------------------------------------------------------- /cmake/static_vector-config.cmake.in: -------------------------------------------------------------------------------- 1 | set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) 2 | 3 | @PACKAGE_INIT@ 4 | 5 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@_targets.cmake") 6 | 7 | check_required_components("@PROJECT_NAME@") 8 | -------------------------------------------------------------------------------- /tests/include/initializer_list_helper.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace ecpp::testing { 5 | 6 | template struct initializer_sequence { 7 | std::initializer_list value; 8 | template constexpr initializer_sequence(std::integer_sequence) : value{Values...} {}; 9 | constexpr initializer_sequence() : initializer_sequence(std::make_integer_sequence{}){}; 10 | std::initializer_list operator()() { return value; } 11 | }; 12 | 13 | } // namespace ecpp::testing 14 | -------------------------------------------------------------------------------- /.cmake-format.json: -------------------------------------------------------------------------------- 1 | { 2 | "line_width": 100, 3 | "tab_size": 2, 4 | "max_subgroups_hwrap": 10, 5 | "separate_ctrl_name_with_space": true, 6 | "separate_fn_name_with_space": false, 7 | "dangle_parens": true, 8 | "line_ending": "unix", 9 | "command_case": "canonical", 10 | "keyword_case": "upper", 11 | "always_wrap": [], 12 | "enable_sort": true, 13 | "autosort": true, 14 | "bullet_char": "*", 15 | "enum_char": ".", 16 | "first_comment_is_literal": true, 17 | "literal_comment_pattern": null, 18 | "fence_pattern": "^\\s*([`~]{3}[`~]*)(.*)$", 19 | "ruler_pattern": "^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$", 20 | "enable_markup": true 21 | } -------------------------------------------------------------------------------- /.github/workflows/cpp_unit_tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build_and_test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: lukka/get-cmake@latest 14 | with: 15 | useLocalCache: true 16 | 17 | - uses: actions/checkout@v4 18 | 19 | - name: Configure 20 | run: cmake --preset default 21 | 22 | - name: Build tests 23 | run: cmake --build --preset testing 24 | 25 | - name: Test 26 | run: ctest --preset testing 27 | 28 | - name: Install 29 | run: cmake --build --preset install 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_policy(SET CMP0135 NEW) 2 | 3 | include(FetchContent) 4 | 5 | FetchContent_Declare( 6 | googletest URL https://github.com/google/googletest/archive/main.zip FIND_PACKAGE_ARGS NAMES 7 | GTest 8 | ) 9 | 10 | FetchContent_MakeAvailable(googletest) 11 | 12 | include(GoogleTest) 13 | 14 | add_executable( 15 | static_vector_ut 16 | 01_construction.cpp 17 | # 02_assignment.cpp 18 | # 03_access.cpp 19 | 04_iterators.cpp 20 | 05_capacity.cpp 21 | 06_modifiers.cpp 22 | 07_non_member_functions.cpp 23 | ) 24 | 25 | target_include_directories(static_vector_ut PUBLIC include) 26 | target_link_libraries(static_vector_ut static_vector GTest::gtest_main) 27 | target_link_options(static_vector_ut PUBLIC -pthread) 28 | 29 | gtest_discover_tests(static_vector_ut) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | project(static_vector LANGUAGES CXX VERSION 1.0.0) 4 | 5 | option(STATIC_VECTOR_TESTS "Configure unit tests" OFF) 6 | option(STATIC_VECTOR_INSTALL "Configure install target" ON) 7 | 8 | add_library(static_vector INTERFACE) 9 | add_library(ecpp::static_vector ALIAS static_vector) 10 | 11 | target_include_directories( 12 | static_vector INTERFACE $ 13 | $ 14 | ) 15 | target_compile_features(static_vector INTERFACE cxx_std_20) 16 | 17 | if (PROJECT_IS_TOP_LEVEL OR STATIC_VECTOR_TESTS) 18 | enable_testing() 19 | add_subdirectory(tests EXCLUDE_FROM_ALL) 20 | endif () 21 | 22 | if (PROJECT_IS_TOP_LEVEL OR STATIC_VECTOR_INSTALL) 23 | include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/install.cmake) 24 | endif () 25 | -------------------------------------------------------------------------------- /cmake/install.cmake: -------------------------------------------------------------------------------- 1 | include(CMakePackageConfigHelpers) 2 | 3 | set(CMAKES_EXPORT_DIR cmake/static_vector) 4 | 5 | write_basic_package_version_file(static_vector-config-version.cmake COMPATIBILITY SameMajorVersion) 6 | 7 | configure_package_config_file( 8 | ${PROJECT_SOURCE_DIR}/cmake/static_vector-config.cmake.in 9 | ${CMAKE_CURRENT_BINARY_DIR}/static_vector-config.cmake INSTALL_DESTINATION ${CMAKES_EXPORT_DIR} 10 | ) 11 | 12 | include(GNUInstallDirs) 13 | install(TARGETS static_vector EXPORT static_vector_targets) 14 | 15 | install(DIRECTORY include/ecpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 16 | 17 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/static_vector-config.cmake 18 | ${CMAKE_CURRENT_BINARY_DIR}/static_vector-config-version.cmake 19 | DESTINATION ${CMAKES_EXPORT_DIR} 20 | ) 21 | 22 | install(EXPORT static_vector_targets NAMESPACE ecpp:: DESTINATION ${CMAKES_EXPORT_DIR}) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jan Macheta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 20, 6 | "patch": 0 7 | }, 8 | "include": [], 9 | "configurePresets": [ 10 | { 11 | "name": "default", 12 | "displayName": "Default", 13 | "description": "Default build configuration", 14 | "generator": "Ninja", 15 | "binaryDir": "${sourceDir}/build", 16 | "installDir": "${sourceDir}/build/install", 17 | "cacheVariables": { 18 | "CMAKE_BUILD_TYPE": "Debug", 19 | "CMAKE_C_FLAGS": "-Werror -Wall -Wextra -Wpedantic -Wunused-parameter -Winit-self -Wcast-align -Wconversion -Wnull-dereference -Wduplicated-cond -Wsign-conversion -Wlogical-op" 20 | } 21 | } 22 | ], 23 | "buildPresets": [ 24 | { 25 | "name": "testing", 26 | "configurePreset": "default", 27 | "targets": [ 28 | "static_vector_ut" 29 | ] 30 | }, 31 | { 32 | "name": "install", 33 | "configurePreset": "default", 34 | "targets": [ 35 | "install" 36 | ] 37 | } 38 | ], 39 | "testPresets": [ 40 | { 41 | "name": "testing", 42 | "configurePreset": "default", 43 | "output": { 44 | "outputOnFailure": true 45 | }, 46 | "execution": { 47 | "noTestsAction": "error", 48 | "stopOnFailure": true 49 | } 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /tests/04_iterators.cpp: -------------------------------------------------------------------------------- 1 | #include "ecpp/static_vector.hpp" 2 | #include "initializer_list_helper.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | using namespace ecpp::testing; 9 | 10 | constexpr std::size_t Capacity{10}; 11 | 12 | TEST(ecpp_static_vector, iterator_begin) { 13 | auto iList = initializer_sequence()(); 14 | 15 | ecpp::static_vector temp(iList); 16 | 17 | auto begin = temp.begin(); 18 | EXPECT_EQ(begin, temp.data()); 19 | 20 | auto cbegin = temp.cbegin(); 21 | EXPECT_EQ(cbegin, temp.data()); 22 | 23 | ecpp::static_vector const tempC(iList); 24 | 25 | auto beginC = tempC.begin(); 26 | EXPECT_EQ(beginC, tempC.data()); 27 | } 28 | 29 | TEST(ecpp_static_vector, iterator_end) { 30 | auto iList = initializer_sequence()(); 31 | 32 | ecpp::static_vector temp(iList); 33 | 34 | auto end = temp.end(); 35 | EXPECT_EQ(end, temp.data() + Capacity); 36 | 37 | auto cend = temp.cend(); 38 | EXPECT_EQ(cend, temp.data() + Capacity); 39 | 40 | ecpp::static_vector const tempC(iList); 41 | 42 | auto endC = tempC.end(); 43 | EXPECT_EQ(endC, tempC.data() + Capacity); 44 | } 45 | 46 | TEST(ecpp_static_vector, iterator_rbegin) { 47 | auto iList = initializer_sequence()(); 48 | 49 | ecpp::static_vector temp(iList); 50 | 51 | auto rbegin = temp.rbegin(); 52 | EXPECT_EQ(rbegin.base(), temp.data() + Capacity); 53 | 54 | ecpp::static_vector const tempC(iList); 55 | 56 | auto rbeginC = tempC.rbegin(); 57 | EXPECT_EQ(rbeginC.base(), tempC.data() + Capacity); 58 | } 59 | 60 | TEST(ecpp_static_vector, iterator_rend) { 61 | auto iList = initializer_sequence()(); 62 | 63 | ecpp::static_vector temp(iList); 64 | 65 | auto rend = temp.rend(); 66 | EXPECT_EQ(rend.base(), temp.data()); 67 | 68 | ecpp::static_vector const tempC(iList); 69 | 70 | auto rendC = tempC.rend(); 71 | EXPECT_EQ(rendC.base(), tempC.data()); 72 | } 73 | -------------------------------------------------------------------------------- /tests/03_access.cpp: -------------------------------------------------------------------------------- 1 | #include "ecpp/static_vector.hpp" 2 | #include "initializer_list_helper.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | using namespace ecpp::testing; 9 | 10 | constexpr std::size_t Capacity{10}; 11 | 12 | TEST(ecpp_static_vector, access_at) { 13 | auto iList = initializer_sequence()(); 14 | 15 | ecpp::static_vector temp(iList); 16 | 17 | for(std::size_t i = 0; i != Capacity; ++i) { 18 | auto &v = temp.at(i); 19 | EXPECT_EQ(std::data(iList)[i], v); 20 | } 21 | 22 | ecpp::static_vector const tempC(iList); 23 | 24 | for(std::size_t i = 0; i != Capacity; ++i) { 25 | auto const &vC = temp.at(i); 26 | EXPECT_EQ(std::data(iList)[i], vC); 27 | } 28 | 29 | #ifdef __cpp_exceptions 30 | EXPECT_THROW(temp.at(Capacity), std::out_of_range); 31 | EXPECT_THROW(tempC.at(Capacity), std::out_of_range); 32 | #else 33 | EXPECT_DEATH(temp.at(Capacity), ""); 34 | EXPECT_DEATH(tempC.at(Capacity), ""); 35 | #endif 36 | } 37 | 38 | TEST(ecpp_static_vector, access_operator) { 39 | auto iList = initializer_sequence()(); 40 | 41 | ecpp::static_vector temp(iList); 42 | 43 | for(std::size_t i = 0; i != Capacity; ++i) { 44 | auto &v = temp[i]; 45 | EXPECT_EQ(std::data(iList)[i], v); 46 | } 47 | 48 | ecpp::static_vector const tempC(iList); 49 | 50 | for(std::size_t i = 0; i != Capacity; ++i) { 51 | auto const &vC = temp[i]; 52 | EXPECT_EQ(std::data(iList)[i], vC); 53 | } 54 | } 55 | 56 | TEST(ecpp_static_vector, access_front) { 57 | auto iList = initializer_sequence()(); 58 | 59 | ecpp::static_vector temp(iList); 60 | auto &v = temp.front(); 61 | EXPECT_EQ(0, v); 62 | 63 | ecpp::static_vector const tempC(iList); 64 | auto const &vC = temp.front(); 65 | EXPECT_EQ(0, vC); 66 | } 67 | 68 | TEST(ecpp_static_vector, access_back) { 69 | auto iList = initializer_sequence()(); 70 | 71 | ecpp::static_vector temp(iList); 72 | auto &v = temp.back(); 73 | EXPECT_EQ(Capacity - 1, v); 74 | 75 | ecpp::static_vector const tempC(iList); 76 | 77 | auto const &vC = temp.back(); 78 | EXPECT_EQ(Capacity - 1, vC); 79 | } 80 | 81 | TEST(ecpp_static_vector, access_data) { 82 | auto iList = initializer_sequence()(); 83 | 84 | ecpp::static_vector temp(iList); 85 | auto data = temp.data(); 86 | for(std::size_t i = 0; i != Capacity; ++i) { 87 | EXPECT_EQ(&temp[i], data + i); 88 | } 89 | 90 | ecpp::static_vector const tempC(iList); 91 | 92 | auto const dataC = temp.data(); 93 | for(std::size_t i = 0; i != Capacity; ++i) { 94 | EXPECT_EQ(&temp[i], dataC + i); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drop-in replacement for std::vector with built-in storage 2 | 3 | This package provides single-header C++20 library that defines container with std::vector interface, which uses statically_allocated storage instead of std::allocator. 4 | 5 | [![Build status](https://github.com/jmacheta/static_vector/actions/workflows/cpp_unit_tests.yml/badge.svg)](https://github.com/jmacheta/static_vector/actions/workflows/cpp_unit_tests.yml) 6 | ## Installation 7 | 8 | The easiest way is to use built-in CMake FetchContent: 9 | 10 | ```cmake 11 | include(FetchContent) 12 | FetchContent_Declare( 13 | ecpp_static_vector 14 | GIT_REPOSITORY https://github.com/jmacheta/static_vector.git 15 | GIT_TAG main 16 | ) 17 | 18 | FetchContent_MakeAvailable(ecpp_static_vector) 19 | ``` 20 | 21 | Then add a dependency to your target: 22 | 23 | ```cmake 24 | target_link_libraries(my_target PUBLIC ecpp_static_vector) 25 | ``` 26 | 27 | ## Usage 28 | 29 | The API of the component is compatible with std::vector one. I tried to implement every method in a meaningful way. 30 | The main difference is a class declaration - You need to provide vector's capacity that will be built into it: 31 | 32 | ```cpp 33 | #include 34 | 35 | ecpp::static_vector vec; // Creates static_vector with capacity to store 20 elements of type int 36 | ``` 37 | 38 | Now you can use it as you would use a std::vector. Please note that in order to avoid UB/throwing, you should check the size before each emplace/insertion: 39 | 40 | ```cpp 41 | if(vec.size() < vec.capacity()) { 42 | vec.push_back(42); 43 | } else { 44 | // Well... do anything but insertion ;) 45 | } 46 | ``` 47 | 48 | If you are not sure, whether you need statically allocated storage, you can create a type alias to switch between static_vector and std::vector as you will: 49 | 50 | ```cpp 51 | inline constexpr std::size_t use_dynamic_storage{std::numeric_limits::max()}; ///< Token to force dynamically allocated storage, instead of static one 52 | 53 | /** 54 | * @brief vector type that may use either statically, or dynamically allocated storage 55 | * 56 | * @tparam T value type 57 | * @tparam capacity Storage capacity (static_vector). When use_dynamic_storage is passed, changes to std::vector with dynamic allocation 58 | */ 59 | template class maybe_static_vector : public std::type_identity> {}; 60 | template class maybe_static_vector : public std::type_identity> {}; 61 | 62 | template using maybe_static_vector_t = typename maybe_static_vector::type; 63 | ``` 64 | 65 | ## Limitations 66 | 67 | - Non-member functions are NOT defined in namespace std. If you need them, use __using namespace__. 68 | - _reserve_ method is a NO-OP (unless you exceed capacity - then it throws) 69 | - shrink_to_fit is a NO-OP 70 | -------------------------------------------------------------------------------- /tests/06_modifiers.cpp: -------------------------------------------------------------------------------- 1 | #include "ecpp/static_vector.hpp" 2 | #include "initializer_list_helper.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | using namespace ecpp::testing; 9 | 10 | constexpr std::size_t Capacity{10}; 11 | 12 | TEST(ecpp_static_vector, modifier_clear) { 13 | auto iList = initializer_sequence()(); 14 | 15 | ecpp::static_vector temp(iList); 16 | EXPECT_EQ(temp.size(), Capacity); 17 | EXPECT_FALSE(temp.empty()); 18 | 19 | temp.clear(); 20 | EXPECT_EQ(temp.size(), 0); 21 | EXPECT_TRUE(temp.empty()); 22 | } 23 | 24 | TEST(ecpp_static_vector, modifier_insert_copy_single) { 25 | static_assert(Capacity >= 4); 26 | 27 | ecpp::static_vector temp; 28 | EXPECT_TRUE(temp.empty()); 29 | 30 | // Insert at the beginning in empty vector 31 | int v1 = 42; 32 | auto pos1 = temp.insert(temp.begin(), v1); 33 | EXPECT_FALSE(temp.empty()); 34 | EXPECT_EQ(temp.size(), 1); 35 | EXPECT_EQ(pos1, temp.begin()); 36 | EXPECT_EQ(*pos1, v1); 37 | 38 | // Insert at the end 39 | int v2 = 44; 40 | auto pos2 = temp.insert(temp.end(), v2); 41 | EXPECT_EQ(temp.size(), 2); 42 | EXPECT_EQ(pos2, temp.begin() + 1); 43 | EXPECT_EQ(*pos2, v2); 44 | 45 | // Insert in between 46 | int v3 = 43; 47 | auto pos3 = temp.insert(pos2, v3); 48 | EXPECT_EQ(temp.size(), 3); 49 | EXPECT_EQ(pos3, temp.begin() + 1); 50 | EXPECT_EQ(*pos3, v3); 51 | EXPECT_EQ(temp.back(), v2); 52 | EXPECT_EQ(temp.front(), v1); 53 | for(auto i : temp) { 54 | std::cout << i << ' '; 55 | } 56 | 57 | // Insert at the beginning in non-empty vector 58 | int v4 = 41; 59 | auto pos4 = temp.insert(temp.begin(), v4); 60 | EXPECT_FALSE(temp.empty()); 61 | EXPECT_EQ(temp.size(), 4); 62 | EXPECT_EQ(pos4, temp.begin()); 63 | EXPECT_EQ(temp.back(), v2); 64 | EXPECT_EQ(temp.front(), v4); 65 | EXPECT_EQ(*pos4, v4); 66 | 67 | int x{}; 68 | // Fill untill full 69 | while(temp.size() < temp.max_size()) { 70 | temp.insert(temp.end(), x); 71 | } 72 | EXPECT_EQ(temp.size(), temp.max_size()); 73 | 74 | #ifdef __cpp_exceptions 75 | EXPECT_THROW(temp.insert(temp.end(), x), std::length_error); 76 | EXPECT_THROW(temp.insert(temp.begin(), x), std::length_error); 77 | #else 78 | EXPECT_DEATH(temp.insert(temp.end(), x), ""); 79 | EXPECT_DEATH(temp.insert(temp.begin(), x), ""); 80 | #endif 81 | } 82 | 83 | TEST(ecpp_static_vector, modifier_insert_move_single) { 84 | // constexpr iterator insert(const_iterator pos, T&& value) == LengthError 85 | } 86 | 87 | TEST(ecpp_static_vector, modifier_insert_move_count) { 88 | // constexpr iterator insert(const_iterator pos, size_type count, T const& value) { LengthError 89 | } 90 | 91 | TEST(ecpp_static_vector, modifier_insert_range) { 92 | // template constexpr iterator insert(const_iterator pos, InputIt first, InputIt last) { LengthError 93 | } 94 | 95 | TEST(ecpp_static_vector, modifier_insert_from_initializer_list) { 96 | // constexpr iterator insert(const_iterator pos, std::initializer_list ilist) { LengthError 97 | } 98 | -------------------------------------------------------------------------------- /tests/05_capacity.cpp: -------------------------------------------------------------------------------- 1 | #include "ecpp/static_vector.hpp" 2 | #include "initializer_list_helper.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | using namespace ecpp::testing; 9 | 10 | constexpr std::size_t Capacity{10}; 11 | 12 | TEST(ecpp_static_vector, capacity_empty) { 13 | ecpp::static_vector tempEmpty; 14 | EXPECT_TRUE(tempEmpty.empty()); 15 | 16 | tempEmpty.push_back({}); 17 | EXPECT_FALSE(tempEmpty.empty()); 18 | 19 | auto iList = initializer_sequence()(); 20 | ecpp::static_vector tempNonEmpty(iList); 21 | 22 | EXPECT_FALSE(tempNonEmpty.empty()); 23 | 24 | tempNonEmpty.clear(); 25 | EXPECT_TRUE(tempNonEmpty.empty()); 26 | } 27 | 28 | TEST(ecpp_static_vector, capacity_size) { 29 | ecpp::static_vector tempEmpty; 30 | EXPECT_EQ(tempEmpty.size(), 0); 31 | 32 | tempEmpty.push_back({}); 33 | EXPECT_EQ(tempEmpty.size(), 1); 34 | 35 | auto iList = initializer_sequence()(); 36 | ecpp::static_vector tempNonEmpty(iList); 37 | EXPECT_EQ(tempNonEmpty.size(), Capacity); 38 | 39 | tempNonEmpty.pop_back(); 40 | EXPECT_EQ(tempNonEmpty.size(), Capacity - 1); 41 | 42 | tempNonEmpty.clear(); 43 | EXPECT_EQ(tempNonEmpty.size(), 0); 44 | } 45 | 46 | TEST(ecpp_static_vector, capacity_max_size) { 47 | ecpp::static_vector temp1; 48 | EXPECT_EQ(temp1.max_size(), Capacity); 49 | 50 | ecpp::static_vector temp2; 51 | EXPECT_EQ(temp2.max_size(), Capacity + 200); 52 | } 53 | 54 | TEST(ecpp_static_vector, capacity_reserve) { 55 | ecpp::static_vector temp1; 56 | EXPECT_EQ(temp1.size(), 0); 57 | EXPECT_EQ(temp1.max_size(), Capacity); 58 | EXPECT_EQ(temp1.capacity(), Capacity); 59 | 60 | temp1.reserve(1); 61 | EXPECT_EQ(temp1.size(), 0); 62 | EXPECT_EQ(temp1.max_size(), Capacity); 63 | EXPECT_EQ(temp1.capacity(), Capacity); 64 | 65 | temp1.reserve(Capacity); 66 | EXPECT_EQ(temp1.size(), 0); 67 | EXPECT_EQ(temp1.max_size(), Capacity); 68 | EXPECT_EQ(temp1.capacity(), Capacity); 69 | 70 | #ifdef __cpp_exceptions 71 | EXPECT_THROW(temp1.reserve(Capacity + 1), std::length_error); 72 | #else 73 | EXPECT_DEATH(temp1.reserve(Capacity + 1), ""); 74 | #endif 75 | } 76 | 77 | TEST(ecpp_static_vector, capacity_capacity) { 78 | ecpp::static_vector temp1; 79 | EXPECT_EQ(temp1.capacity(), Capacity); 80 | 81 | ecpp::static_vector temp2; 82 | EXPECT_EQ(temp2.capacity(), Capacity + 200); 83 | } 84 | 85 | TEST(ecpp_static_vector, capacity_shrink_to_fit) { 86 | ecpp::static_vector temp1; 87 | EXPECT_EQ(temp1.size(), 0); 88 | EXPECT_EQ(temp1.capacity(), Capacity); 89 | 90 | temp1.shrink_to_fit(); 91 | 92 | EXPECT_EQ(temp1.size(), 0); 93 | EXPECT_LE(temp1.capacity(), Capacity); 94 | 95 | auto iList = initializer_sequence()(); 96 | ecpp::static_vector temp2(iList); 97 | EXPECT_EQ(temp2.size(), Capacity - 1); 98 | EXPECT_EQ(temp2.capacity(), Capacity); 99 | 100 | temp2.shrink_to_fit(); 101 | 102 | EXPECT_EQ(temp2.size(), Capacity - 1); 103 | EXPECT_LE(temp2.capacity(), Capacity); 104 | } 105 | -------------------------------------------------------------------------------- /tests/07_non_member_functions.cpp: -------------------------------------------------------------------------------- 1 | #include "ecpp/static_vector.hpp" 2 | #include "initializer_list_helper.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | 9 | using namespace ecpp::testing; 10 | 11 | constexpr std::size_t Capacity{10}; 12 | 13 | struct NonMovableType { 14 | int value = 0; 15 | NonMovableType() = default; 16 | NonMovableType(int val) : value(val){}; 17 | auto& operator=(NonMovableType const&) = delete; 18 | }; 19 | 20 | 21 | TEST(ecpp_static_vector, non_member_equality_comparison_operator) { 22 | ecpp::static_vector r1{1, 0, 1, 0, 1, 0, 2, 2, 1, 3}; 23 | auto r1c = r1; // Exact copy 24 | ecpp::static_vector r2{1, 0, 1, 0, 1, 0, 2, 2, 1, 4}; // Last element different 25 | ecpp::static_vector r3{1, 0, 1, 0, 1, 0, 2, 2, 1}; // One element less 26 | 27 | EXPECT_TRUE(r1 == r1c); 28 | EXPECT_FALSE(r1 == r2); 29 | EXPECT_FALSE(r1 == r3); 30 | EXPECT_FALSE(r2 == r3); 31 | 32 | // Modify r1 to remove last element 33 | r1.pop_back(); 34 | EXPECT_FALSE(r1 == r1c); 35 | EXPECT_FALSE(r1 == r2); 36 | EXPECT_TRUE(r1 == r3); 37 | } 38 | 39 | 40 | TEST(ecpp_static_vector, non_member_three_way_comparison_operator) { 41 | ecpp::static_vector r1{1, 0, 1, 0, 1, 0, 2, 2, 1, 3}; 42 | auto r1c = r1; // Exact copy 43 | ecpp::static_vector r2{1, 0, 1, 0, 1, 0, 2, 2, 1, 4}; // Last element different 44 | ecpp::static_vector r3{1, 0, 1, 0, 1, 0, 2, 2, 1}; // One element less 45 | 46 | EXPECT_EQ(r1 <=> r1c, std::strong_ordering::equivalent); 47 | EXPECT_EQ(r1 <=> r2, std::strong_ordering::less); 48 | EXPECT_EQ(r1 <=> r3, std::strong_ordering::greater); 49 | EXPECT_EQ(r2 <=> r3, std::strong_ordering::greater); 50 | 51 | 52 | // Modify r1 to remove last element 53 | r1.pop_back(); 54 | EXPECT_NE(r1 <=> r1c, std::strong_ordering::equivalent); 55 | EXPECT_EQ(r1 <=> r1c, std::strong_ordering::less); 56 | EXPECT_EQ(r1 <=> r2, std::strong_ordering::less); 57 | EXPECT_EQ(r1 <=> r3, std::strong_ordering::equivalent); 58 | } 59 | 60 | 61 | TEST(ecpp_static_vector, non_member_swap) { 62 | ecpp::static_vector r1{1, 0, 1, 0, 1, 0, 2, 2, 1, 3}; 63 | auto r1c = r1; 64 | 65 | ecpp::static_vector r2{3, 7}; 66 | auto r2c = r2; 67 | 68 | ecpp::swap(r1, r2); 69 | 70 | EXPECT_EQ(r1, r2c); 71 | EXPECT_EQ(r2, r1c); 72 | } 73 | 74 | 75 | TEST(ecpp_static_vector, non_member_erase) { 76 | ecpp::static_vector before{1, 0, 1, 0, 1, 0, 2, 2, 1, 3}; 77 | 78 | EXPECT_EQ(before.size(), 10); 79 | ecpp::erase(before, 4); // Should remove nothing 80 | EXPECT_EQ(before.size(), 10); 81 | EXPECT_EQ(before, before); 82 | 83 | ecpp::erase(before, 0); // Should remove 3 elements 84 | ecpp::static_vector expected{1, 1, 1, 2, 2, 1, 3}; 85 | EXPECT_EQ(before.size(), 7); 86 | EXPECT_EQ(before, expected); 87 | } 88 | 89 | 90 | TEST(ecpp_static_vector, non_member_erase_if) { 91 | ecpp::static_vector before{1, 0, 1, 0, 1, 0, 2, 2, 1, 3}; 92 | 93 | EXPECT_EQ(before.size(), 10); 94 | ecpp::erase_if(before, [](auto const& i) { return i == 4; }); // Should remove nothing 95 | EXPECT_EQ(before.size(), 10); 96 | EXPECT_EQ(before, before); 97 | 98 | ecpp::erase_if(before, [](auto const& i) { return i == 0; }); // Should remove 3 elements 99 | ecpp::static_vector expected{1, 1, 1, 2, 2, 1, 3}; 100 | EXPECT_EQ(before.size(), 7); 101 | EXPECT_EQ(before, expected); 102 | 103 | ecpp::erase_if(before, [](auto const& i) { return i == 1 || i == 2 || i == 3; }); // Should remove remaining elements 104 | EXPECT_EQ(before.size(), 0); 105 | 106 | ecpp::static_vector before2{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 107 | EXPECT_EQ(before2.size(), 10); 108 | ecpp::erase_if(before, [](auto const& i) { return i == 11; }); // Should remove nothing 109 | EXPECT_EQ(before2.size(), 10); 110 | } 111 | -------------------------------------------------------------------------------- /tests/01_construction.cpp: -------------------------------------------------------------------------------- 1 | #include "ecpp/static_vector.hpp" 2 | #include "initializer_list_helper.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | using namespace ecpp::testing; 9 | 10 | constexpr std::size_t Capacity{10}; 11 | TEST(ecpp_static_vector, construction_default) { 12 | ecpp::static_vector temp; 13 | EXPECT_EQ(temp.max_size(), Capacity); 14 | EXPECT_EQ(temp.capacity(), Capacity); 15 | EXPECT_EQ(temp.size(), 0); 16 | } 17 | 18 | TEST(ecpp_static_vector, construction_with_count_copies) { 19 | ecpp::static_vector temp(Capacity, -1); 20 | EXPECT_EQ(temp.max_size(), Capacity); 21 | EXPECT_EQ(temp.capacity(), Capacity); 22 | EXPECT_EQ(temp.size(), Capacity); 23 | 24 | for(auto i : temp) { 25 | EXPECT_EQ(i, -1); 26 | } 27 | 28 | auto tryConstructInvalidVector = []() { ecpp::static_vector _(Capacity + 1, {42}); }; 29 | #ifdef __cpp_exceptions 30 | EXPECT_THROW(tryConstructInvalidVector(), std::length_error); 31 | #else 32 | EXPECT_DEATH(tryConstructInvalidVector(), ""); 33 | #endif 34 | } 35 | 36 | TEST(ecpp_static_vector, construction_with_count_default_inserted_instances) { 37 | ecpp::static_vector temp(Capacity); 38 | EXPECT_EQ(temp.max_size(), Capacity); 39 | EXPECT_EQ(temp.capacity(), Capacity); 40 | EXPECT_EQ(temp.size(), Capacity); 41 | 42 | auto tryConstructInvalidVector = []() { ecpp::static_vector _(Capacity + 1); }; 43 | #ifdef __cpp_exceptions 44 | EXPECT_THROW(tryConstructInvalidVector(), std::length_error); 45 | #else 46 | EXPECT_DEATH(tryConstructInvalidVector(), ""); 47 | #endif 48 | } 49 | 50 | TEST(ecpp_static_vector, construction_from_range) { 51 | auto iList = initializer_sequence()(); 52 | std::vector temp_src(iList); 53 | 54 | ecpp::static_vector temp_dst(temp_src.begin(), temp_src.end()); 55 | 56 | EXPECT_EQ(temp_dst.max_size(), Capacity); 57 | EXPECT_EQ(temp_dst.capacity(), Capacity); 58 | EXPECT_EQ(temp_dst.size(), temp_src.size()); 59 | 60 | EXPECT_TRUE(std::equal(temp_src.begin(), temp_src.end(), temp_dst.begin(), temp_dst.end())); 61 | 62 | auto tryConstructInvalidVector = []() { 63 | auto iListTooBig = initializer_sequence()(); 64 | std::vector temp_src(iListTooBig); 65 | ecpp::static_vector _(temp_src.begin(), temp_src.end()); 66 | }; 67 | #ifdef __cpp_exceptions 68 | EXPECT_THROW(tryConstructInvalidVector(), std::length_error); 69 | #else 70 | EXPECT_DEATH(tryConstructInvalidVector(), ""); 71 | #endif 72 | } 73 | 74 | TEST(ecpp_static_vector, construction_copy) { 75 | ecpp::static_vector temp_src(Capacity, -1); 76 | 77 | ecpp::static_vector temp_dst(temp_src); 78 | 79 | EXPECT_EQ(temp_dst.max_size(), temp_src.max_size()); 80 | EXPECT_EQ(temp_dst.capacity(), temp_src.capacity()); 81 | EXPECT_EQ(temp_dst.size(), temp_src.size()); 82 | 83 | EXPECT_EQ(temp_src, temp_dst); 84 | } 85 | 86 | TEST(ecpp_static_vector, construction_move) { 87 | ecpp::static_vector temp_src(Capacity, -1); 88 | ecpp::static_vector temp_dst(std::move(temp_src)); 89 | 90 | EXPECT_EQ(temp_dst.max_size(), temp_src.max_size()); 91 | EXPECT_EQ(temp_dst.capacity(), temp_src.capacity()); 92 | EXPECT_EQ(temp_dst.size(), temp_src.size()); 93 | 94 | for(auto i : temp_dst) { 95 | EXPECT_EQ(i, -1); 96 | } 97 | 98 | // The equality of elements of both vectors MAY not be met when elements are not trivial 99 | } 100 | 101 | // TEST(ecpp_static_vector, construction_from_initializer_list) { 102 | // auto iList = initializer_sequence()(); 103 | 104 | // ecpp::static_vector temp(iList); 105 | // EXPECT_EQ(temp.max_size(), Capacity); 106 | // EXPECT_EQ(temp.capacity(), Capacity); 107 | // EXPECT_EQ(temp.size(), Capacity); 108 | 109 | // EXPECT_TRUE(std::equal(temp.begin(), temp.end(), iList.begin(), iList.end())); 110 | 111 | // auto tryConstructInvalidVector = []() { 112 | // auto iListTooBig = initializer_sequence()(); 113 | // ecpp::static_vector _(iListTooBig); 114 | // }; 115 | // #ifdef __cpp_exceptions 116 | // EXPECT_THROW(tryConstructInvalidVector(), std::length_error); 117 | // #else 118 | // EXPECT_DEATH(tryConstructInvalidVector(), ""); 119 | // #endif 120 | // } 121 | -------------------------------------------------------------------------------- /tests/02_assignment.cpp: -------------------------------------------------------------------------------- 1 | #include "ecpp/static_vector.hpp" 2 | #include "initializer_list_helper.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | using namespace ecpp::testing; 9 | 10 | constexpr std::size_t Capacity{10}; 11 | 12 | TEST(ecpp_static_vector, assignment_with_copy_assignment_operator) { 13 | auto iList = initializer_sequence()(); 14 | ecpp::static_vector temp_src(iList); 15 | 16 | // Case when destination is empty 17 | ecpp::static_vector temp_dst1; 18 | EXPECT_EQ(temp_dst1.size(), 0); 19 | 20 | temp_dst1 = temp_src; 21 | 22 | EXPECT_EQ(temp_dst1.size(), Capacity); 23 | EXPECT_EQ(temp_dst1, temp_src); 24 | 25 | // Case when destination is non-empty 26 | ecpp::static_vector temp_dst2(1); 27 | EXPECT_EQ(temp_dst2.size(), 1); 28 | 29 | temp_dst2 = temp_src; 30 | 31 | EXPECT_EQ(temp_dst2.size(), Capacity); 32 | EXPECT_EQ(temp_dst2, temp_src); 33 | } 34 | 35 | TEST(ecpp_static_vector, assignment_with_move_assignment_operator) { 36 | auto iList = initializer_sequence()(); 37 | ecpp::static_vector temp_src1(iList); 38 | ecpp::static_vector temp_src2(iList); 39 | 40 | // Case when destination is empty 41 | ecpp::static_vector temp_dst1; 42 | EXPECT_EQ(temp_dst1.size(), 0); 43 | 44 | temp_dst1 = std::move(temp_src1); 45 | 46 | EXPECT_EQ(temp_dst1.size(), Capacity); 47 | EXPECT_TRUE(std::equal(temp_dst1.begin(), temp_dst1.end(), iList.begin(), iList.end())); 48 | 49 | // Case when destination is non-empty 50 | ecpp::static_vector temp_dst2(1); 51 | EXPECT_EQ(temp_dst2.size(), 1); 52 | 53 | temp_dst2 = std::move(temp_src2); 54 | 55 | EXPECT_EQ(temp_dst2.size(), Capacity); 56 | EXPECT_TRUE(std::equal(temp_dst2.begin(), temp_dst2.end(), iList.begin(), iList.end())); 57 | } 58 | 59 | TEST(ecpp_static_vector, assignment_with_operator_from_initializer_list) { 60 | auto iList = initializer_sequence()(); 61 | 62 | // Case when destination is empty 63 | ecpp::static_vector temp1; 64 | EXPECT_EQ(temp1.size(), 0); 65 | 66 | temp1 = iList; 67 | 68 | EXPECT_EQ(temp1.size(), Capacity); 69 | EXPECT_TRUE(std::equal(temp1.begin(), temp1.end(), iList.begin(), iList.end())); 70 | 71 | // Case when destination is non-empty 72 | ecpp::static_vector temp2(1); 73 | EXPECT_EQ(temp2.size(), 1); 74 | 75 | temp2 = iList; 76 | 77 | EXPECT_EQ(temp2.size(), Capacity); 78 | EXPECT_TRUE(std::equal(temp2.begin(), temp2.end(), iList.begin(), iList.end())); 79 | 80 | auto tryAssignToLargeList = []() { 81 | auto iListTooBig = initializer_sequence()(); 82 | ecpp::static_vector _; 83 | _ = iListTooBig; 84 | }; 85 | #ifdef __cpp_exceptions 86 | EXPECT_THROW(tryAssignToLargeList(), std::length_error); 87 | #else 88 | EXPECT_DEATH(tryAssignToLargeList(), ""); 89 | #endif 90 | } 91 | 92 | TEST(ecpp_static_vector, assignment_with_assign_count_copies) { 93 | // Case when destination is empty 94 | ecpp::static_vector temp1; 95 | EXPECT_EQ(temp1.size(), 0); 96 | 97 | temp1.assign(Capacity, 1); 98 | 99 | EXPECT_EQ(temp1.size(), Capacity); 100 | EXPECT_EQ(std::count(temp1.begin(), temp1.end(), 1), Capacity); 101 | 102 | // Case when destination is non-empty 103 | ecpp::static_vector temp2(1); 104 | EXPECT_EQ(temp2.size(), 1); 105 | 106 | temp2.assign(Capacity, 1); 107 | 108 | EXPECT_EQ(temp2.size(), Capacity); 109 | EXPECT_EQ(std::count(temp2.begin(), temp2.end(), 1), Capacity); 110 | 111 | auto tryAssignToLargeList = []() { 112 | ecpp::static_vector _; 113 | _.assign(Capacity + 1, 1); 114 | }; 115 | #ifdef __cpp_exceptions 116 | EXPECT_THROW(tryAssignToLargeList(), std::length_error); 117 | #else 118 | EXPECT_DEATH(tryAssignToLargeList(), ""); 119 | #endif 120 | } 121 | 122 | TEST(ecpp_static_vector, assignment_with_assign_from_range) { 123 | auto iList = initializer_sequence()(); 124 | std::vector temp_src(iList); 125 | 126 | // Case when destination is empty 127 | ecpp::static_vector temp_dst1; 128 | EXPECT_EQ(temp_dst1.size(), 0); 129 | 130 | temp_dst1.assign(temp_src.begin(), temp_src.end()); 131 | 132 | EXPECT_EQ(temp_dst1.size(), Capacity); 133 | EXPECT_TRUE(std::equal(temp_dst1.begin(), temp_dst1.end(), temp_src.begin(), temp_src.end())); 134 | 135 | // Case when destination is non-empty 136 | ecpp::static_vector temp_dst2(1); 137 | EXPECT_EQ(temp_dst2.size(), 1); 138 | 139 | temp_dst2.assign(temp_src.begin(), temp_src.end()); 140 | 141 | EXPECT_EQ(temp_dst2.size(), Capacity); 142 | EXPECT_TRUE(std::equal(temp_dst2.begin(), temp_dst2.end(), temp_src.begin(), temp_src.end())); 143 | 144 | auto tryAssignToLargeRange = []() { 145 | auto iListTooLarge = initializer_sequence()(); 146 | std::vector temp_src(iListTooLarge); 147 | ecpp::static_vector _; 148 | _.assign(temp_src.begin(), temp_src.end()); 149 | }; 150 | #ifdef __cpp_exceptions 151 | EXPECT_THROW(tryAssignToLargeRange(), std::length_error); 152 | #else 153 | EXPECT_DEATH(tryAssignToLargeRange(), ""); 154 | #endif 155 | } 156 | 157 | TEST(ecpp_static_vector, assignment_with_assign_from_initializer_list) { 158 | auto iList = initializer_sequence()(); 159 | 160 | // Case when destination is empty 161 | ecpp::static_vector temp1; 162 | EXPECT_EQ(temp1.size(), 0); 163 | 164 | temp1.assign(iList); 165 | 166 | EXPECT_EQ(temp1.size(), Capacity); 167 | EXPECT_TRUE(std::equal(temp1.begin(), temp1.end(), iList.begin(), iList.end())); 168 | 169 | // Case when destination is non-empty 170 | ecpp::static_vector temp2(1); 171 | EXPECT_EQ(temp2.size(), 1); 172 | 173 | temp2.assign(iList); 174 | 175 | EXPECT_EQ(temp2.size(), Capacity); 176 | EXPECT_TRUE(std::equal(temp2.begin(), temp2.end(), iList.begin(), iList.end())); 177 | 178 | auto tryAssignToLargeList = []() { 179 | auto iListTooBig = initializer_sequence()(); 180 | ecpp::static_vector _; 181 | _.assign(iListTooBig); 182 | }; 183 | #ifdef __cpp_exceptions 184 | EXPECT_THROW(tryAssignToLargeList(), std::length_error); 185 | #else 186 | EXPECT_DEATH(tryAssignToLargeList(), ""); 187 | #endif 188 | } 189 | -------------------------------------------------------------------------------- /include/ecpp/static_vector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_VECTOR_HPP_ 2 | #define STATIC_VECTOR_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef __cpp_exceptions 13 | #include 14 | #define ECPP_STATIC_VECTOR_THROW(x) throw(x) 15 | #else 16 | #define ECPP_STATIC_VECTOR_THROW(x) std::abort(); 17 | #endif 18 | 19 | namespace ecpp { 20 | 21 | /** 22 | * @brief Implementation of standard vector with statically allocated pool 23 | * The container uses underlying array to allocate required capacity. 24 | * For detailed documentation see https://en.cppreference.com/w/cpp/container/vector 25 | * The documentation below highlights only the differences. 26 | */ 27 | template class static_vector { 28 | public: 29 | using value_type = T; ///< The type of the elements 30 | using storage_container_type = std::array, N>; ///< Type of underlying continuous static storage 31 | using size_type = typename storage_container_type::size_type; ///< Unsigned integer type (usually std::size_t) 32 | 33 | using pointer = T *; ///< Pointer to element type 34 | using const_pointer = T const *; ///< Const pointer to element type 35 | 36 | using reference = T &; ///< Reference to element type 37 | using const_reference = T const &; ///< Const reference to element type 38 | 39 | using iterator = pointer; ///< Iterator to value_type type that meets LegacyRandomAccessIterator requirements 40 | using const_iterator = const_pointer; ///< Iterator to const value_type type that meets LegacyRandomAccessIterator requirements 41 | 42 | using reverse_iterator = std::reverse_iterator; ///< Reverse iterator type 43 | using const_reverse_iterator = std::reverse_iterator; ///< Reverse const iterator type 44 | 45 | using difference_type = 46 | decltype(std::distance(std::declval(), std::declval())); ///< Signed integer type (usually std::ptrdiff_t) 47 | 48 | private: 49 | storage_container_type container; ///< element storage 50 | size_type currentSize{0}; ///< current element count 51 | 52 | public: 53 | /// Default constructor. Constructs an empty container with a default-constructed allocator 54 | constexpr static_vector() noexcept = default; 55 | 56 | /** 57 | * @brief Constructs the container with count copies of elements with value value 58 | * @param[in] count the size of the container 59 | * @param[in] value the value to initialize elements of the container with 60 | * @note The method will throw LengthError if count > max_size() 61 | */ 62 | constexpr static_vector(size_type count, const_reference value) : currentSize(count) { 63 | if(count > max_size()) { 64 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 65 | } 66 | std::uninitialized_fill_n(begin(), count, value); 67 | } 68 | 69 | /** 70 | * @brief Constructs the container with count default-inserted instances of T. No copies are made 71 | * @param[in] count the size of the container 72 | * @note The method will throw LengthError if count > max_size() 73 | */ 74 | constexpr explicit static_vector(size_type count) 75 | requires(std::is_default_constructible_v) 76 | : currentSize(count) { 77 | if(count > max_size()) { 78 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 79 | } 80 | std::uninitialized_default_construct_n(begin(), count); 81 | } 82 | 83 | /** 84 | * @brief Constructs the container with the contents of the range [first, last) 85 | * @param[in] first start of the range to copy the elements from 86 | * @param[in] last end of the range to copy the elements from 87 | * @note The method will throw LengthError if std::distance(first, last) is negative or, greater than max_size() 88 | */ 89 | template constexpr static_vector(InputIt first, InputIt last) { 90 | auto dist = std::distance(first, last); 91 | auto count = size_type(dist); 92 | if(dist < 0 || count > max_size()) { 93 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 94 | } 95 | currentSize = count; 96 | std::uninitialized_copy(first, last, begin()); 97 | } 98 | 99 | /** 100 | * @brief Copy constructor. Constructs the container with the copy of the contents of other 101 | * @param[in] other another container to be used as source to initialize the elements of the container with 102 | */ 103 | constexpr static_vector(static_vector const &other) : currentSize(other.currentSize) { 104 | std::uninitialized_copy(other.begin(), other.end(), begin()); 105 | } 106 | 107 | /** 108 | * @brief Move constructor. Constructs the container with the contents of other using move semantics. After the move, other is guaranteed to be 109 | * empty() 110 | * @param[in] other another container to be used as source to initialize the elements of the container with 111 | */ 112 | constexpr static_vector(static_vector &&other) noexcept 113 | requires(std::movable) 114 | : currentSize(std::move(other.currentSize)) { 115 | std::uninitialized_move(other.begin(), other.end(), begin()); 116 | } 117 | 118 | /** 119 | * @brief Constructs the container with the contents of the initializer list init 120 | * @param[in] init initializer list to initialize the elements of the container with 121 | * @note The method will throw LengthError if init.size() > max_size() 122 | */ 123 | constexpr static_vector(std::initializer_list init) : static_vector(init.begin(), init.end()) {} 124 | 125 | /** 126 | * @brief Destructs the vector. The destructors of the elements are called and the used storage is deallocated. 127 | * @note if the elements are pointers, the pointed-to objects are not destroyed. 128 | */ 129 | constexpr ~static_vector() noexcept { 130 | if constexpr(!std::is_trivially_destructible_v) { 131 | clear(); 132 | } 133 | } 134 | 135 | /** 136 | * @brief Copy assignment operator. Replaces the contents with a copy of the contents of other 137 | * @param[in] other another container to use as data source 138 | * @return *this 139 | */ 140 | constexpr static_vector &operator=(static_vector const &other) { 141 | if(&other != this) { 142 | if(!empty()) { 143 | clear(); 144 | } 145 | currentSize = other.currentSize; 146 | std::uninitialized_copy(other.begin(), other.end(), begin()); 147 | } 148 | return *this; 149 | } 150 | 151 | /** 152 | * @brief Move assignment operator. Replaces the contents with those of other using move semantics (i.e. the data in other is moved from other into 153 | * this container). other is in a valid but unspecified state afterwards 154 | * @param[in] other another container to use as data source 155 | * @return *this 156 | */ 157 | constexpr static_vector &operator=(static_vector &&other) { 158 | if(&other != this) { 159 | if(!empty()) { 160 | clear(); 161 | } 162 | currentSize = std::move(other.currentSize); 163 | std::uninitialized_move(other.begin(), other.end(), begin()); 164 | } 165 | return *this; 166 | } 167 | 168 | /** 169 | * @brief Replaces the contents with those identified by initializer list ilist 170 | * @param[in] ilist initializer list to use as data source 171 | * @return *this 172 | * @note The method will throw LengthError if ilist.size() > max_size() 173 | */ 174 | constexpr static_vector &operator=(std::initializer_list ilist) { 175 | if(!empty()) { 176 | clear(); 177 | } 178 | if(ilist.size() > max_size()) { 179 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 180 | } 181 | currentSize = ilist.size(); 182 | std::uninitialized_copy(ilist.begin(), ilist.end(), begin()); 183 | return *this; 184 | } 185 | 186 | /** 187 | * @brief Replaces the contents with count copies of value value 188 | * @param[in] count the new size of the container 189 | * @param[in] value the value to initialize elements of the container with 190 | * @note The method will throw LengthError if count > max_size() 191 | */ 192 | constexpr void assign(size_type count, T const &value) { 193 | if(!empty()) { 194 | clear(); 195 | } 196 | if(count > max_size()) { 197 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 198 | } 199 | std::uninitialized_fill_n(begin(), count, value); 200 | currentSize = count; 201 | } 202 | 203 | /** 204 | * @brief Replaces the contents with copies of those in the range [first, last) 205 | * @param[in] first start of the range to copy the elements from 206 | * @param[in] last end of the range to copy the elements from 207 | * @note The behavior is undefined if either argument is an iterator into *this. 208 | * @note The method will throw LengthError if std::distance(first, last) is negative or, greater than max_size() 209 | */ 210 | template constexpr void assign(InputIt first, InputIt last) { 211 | if(!empty()) { 212 | clear(); 213 | } 214 | 215 | auto dist = std::distance(first, last); 216 | auto count = size_type(dist); 217 | if(dist < 0 || count > max_size()) { 218 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 219 | } 220 | currentSize = count; 221 | std::uninitialized_copy(first, last, begin()); 222 | } 223 | 224 | /** 225 | * @brief Replaces the contents with the elements from the initializer list ilist 226 | * @param[in] ilist initializer list to copy the values from 227 | * @note The method will throw LengthError if ilist.size() > max_size() 228 | */ 229 | constexpr void assign(std::initializer_list ilist) { assign(ilist.begin(), ilist.end()); } 230 | 231 | /** 232 | * @brief Returns a reference to the element at specified location pos, with bounds checking. 233 | * @param[in] pos position of the element to return 234 | * @return Reference to the requested element 235 | * @note If pos is not within the range of the container, an OutOfRangeError is thrown. 236 | */ 237 | constexpr reference at(size_type pos) { 238 | if(pos >= size()) { 239 | ECPP_STATIC_VECTOR_THROW(std::out_of_range("Index out of bounds")); 240 | } 241 | return (*this)[pos]; 242 | } 243 | 244 | /** 245 | * @brief Returns a reference to the element at specified location pos, with bounds checking. 246 | * @param[in] pos position of the element to return 247 | * @return Reference to the requested element 248 | * @note If pos is not within the range of the container, an OutOfRangeError is thrown. 249 | */ 250 | constexpr const_reference at(size_type pos) const { 251 | if(pos >= size()) { 252 | ECPP_STATIC_VECTOR_THROW(std::out_of_range("Index out of bounds")); 253 | } 254 | return (*this)[pos]; 255 | } 256 | 257 | /** 258 | * @brief Returns a reference to the element at specified location pos. No bounds checking is performed 259 | * @param[in] pos position of the element to return 260 | * @return Reference to the requested element 261 | * @note Accessing a nonexistent element through this operator is undefined behavior 262 | */ 263 | constexpr reference operator[](size_type pos) noexcept { return *(begin() + pos); } 264 | 265 | /** 266 | * @brief Returns a reference to the element at specified location pos. No bounds checking is performed 267 | * @param[in] pos position of the element to return 268 | * @return Reference to the requested element 269 | * @note Accessing a nonexistent element through this operator is undefined behavior 270 | */ 271 | constexpr const_reference operator[](size_type pos) const noexcept { return *(begin() + pos); } 272 | 273 | /** 274 | * @brief Returns a reference to the first element in the container 275 | * @return Reference to the first element 276 | * @note Calling front on an empty container is undefined 277 | */ 278 | constexpr reference front() noexcept { return *begin(); } 279 | 280 | /** 281 | * @brief Returns a reference to the first element in the container 282 | * @return Reference to the first element 283 | * @note Calling front on an empty container is undefined 284 | */ 285 | constexpr const_reference front() const noexcept { return *begin(); } 286 | 287 | /** 288 | * @brief Returns a reference to the last element in the container 289 | * @return Reference to the last element 290 | * @note Calling back on an empty container is undefined 291 | */ 292 | constexpr reference back() noexcept { return *(end() - 1); } 293 | 294 | /** 295 | * @brief Returns a reference to the last element in the container 296 | * @return Reference to the last element 297 | * @note Calling back on an empty container is undefined 298 | */ 299 | constexpr const_reference back() const noexcept { return *(end() - 1); } 300 | 301 | /** 302 | * @brief Returns pointer to the underlying array serving as element storage 303 | * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal to the address of the first 304 | * element 305 | * @note The pointer is such that range [data(); data() + size()) is always a valid range, even if the container is empty (data() is not 306 | * dereferenceable in that case) 307 | */ 308 | constexpr pointer data() noexcept { return std::launder(reinterpret_cast(container.begin())); } 309 | 310 | /** 311 | * @brief Returns pointer to the underlying array serving as element storage 312 | * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal to the address of the first 313 | * element 314 | * @note The pointer is such that range [data(); data() + size()) is always a valid range, even if the container is empty (data() is not 315 | * dereferenceable in that case) 316 | */ 317 | constexpr const_pointer data() const noexcept { return std::launder(reinterpret_cast(container.begin())); } 318 | 319 | /** 320 | * @brief Returns an iterator to the first element of the vector. 321 | * If the vector is empty, the returned iterator will be equal to end() 322 | * @return Iterator to the first element 323 | */ 324 | constexpr iterator begin() noexcept { return data(); } 325 | 326 | /** 327 | * @brief Returns an iterator to the first element of the vector. 328 | * If the vector is empty, the returned iterator will be equal to end() 329 | * @return Iterator to the first element 330 | */ 331 | constexpr const_iterator begin() const noexcept { return data(); } 332 | 333 | /** 334 | * @brief Returns an iterator to the first element of the vector. 335 | * If the vector is empty, the returned iterator will be equal to end() 336 | * @return Iterator to the first element 337 | */ 338 | constexpr const_iterator cbegin() const noexcept { return data(); } 339 | 340 | /** 341 | * @brief Returns an iterator to the element following the last element of the vector 342 | * @return Iterator to the element following the last element 343 | * @note This element acts as a placeholder; attempting to access it results in undefined behavior. 344 | */ 345 | constexpr iterator end() noexcept { return begin() + currentSize; } 346 | 347 | /** 348 | * @brief Returns an iterator to the element following the last element of the vector 349 | * @return Iterator to the element following the last element 350 | * @note This element acts as a placeholder; attempting to access it results in undefined behavior. 351 | */ 352 | constexpr const_iterator end() const noexcept { return begin() + currentSize; } 353 | 354 | /** 355 | * @brief Returns an iterator to the element following the last element of the vector 356 | * @return Iterator to the element following the last element 357 | * @note This element acts as a placeholder; attempting to access it results in undefined behavior. 358 | */ 359 | constexpr const_iterator cend() const noexcept { return begin() + currentSize; } 360 | 361 | /** 362 | * @brief Returns a reverse iterator to the first element of the reversed vector. 363 | * It corresponds to the last element of the non-reversed vector. If the vector is empty, the returned iterator is equal to rend(). 364 | * @return Reverse iterator to the first element 365 | */ 366 | constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } 367 | 368 | /** 369 | * @brief Returns a reverse iterator to the first element of the reversed vector. 370 | * It corresponds to the last element of the non-reversed vector. If the vector is empty, the returned iterator is equal to rend(). 371 | * @return Reverse iterator to the first element 372 | */ 373 | constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } 374 | 375 | /** 376 | * @brief Returns a reverse iterator to the first element of the reversed vector. 377 | * It corresponds to the last element of the non-reversed vector. If the vector is empty, the returned iterator is equal to rend(). 378 | * @return Reverse iterator to the first element 379 | */ 380 | constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } 381 | 382 | /** 383 | * @brief Returns a reverse iterator to the element following the last element of the reversed vector. 384 | * It corresponds to the element preceding the first element of the non-reversed vector 385 | * @return Reverse iterator to the element following the last element 386 | * @note This element acts as a placeholder, attempting to access it results in undefined behavior 387 | */ 388 | constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); } 389 | 390 | /** 391 | * @brief Returns a reverse iterator to the element following the last element of the reversed vector. 392 | * It corresponds to the element preceding the first element of the non-reversed vector 393 | * @return Reverse iterator to the element following the last element 394 | * @note This element acts as a placeholder, attempting to access it results in undefined behavior 395 | */ 396 | constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } 397 | 398 | /** 399 | * @brief Returns a reverse iterator to the element following the last element of the reversed vector. 400 | * It corresponds to the element preceding the first element of the non-reversed vector 401 | * @return Reverse iterator to the element following the last element 402 | * @note This element acts as a placeholder, attempting to access it results in undefined behavior 403 | */ 404 | constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } 405 | 406 | /** 407 | * @brief Checks if the container has no elements, i.e. whether begin() == end() 408 | * @return true if the container is empty, false otherwise 409 | */ 410 | constexpr bool empty() const noexcept { return 0 == size(); } 411 | 412 | /** 413 | * @brief Returns the number of elements in the container, i.e. std::distance(begin(), end()) 414 | * @return The number of elements in the container 415 | */ 416 | constexpr size_type size() const noexcept { return currentSize; } 417 | 418 | /** 419 | * @brief Returns the maximum number of elements the container is able to hold due to system or library implementation limitations i.e. N 420 | * @return Maximum number of elements 421 | */ 422 | constexpr size_type max_size() const noexcept { return container.max_size(); } 423 | 424 | /** 425 | * @brief Increase the capacity of the vector to a value that's greater or equal to new_cap. 426 | * Because the static_vector uses static storage, when new_cap <= max_size() the method does nothing. 427 | * @param[in] new_cap new capacity of the vector 428 | * @note For compatibility purposes, The method throws LengthError when new_cap > max_size 429 | */ 430 | constexpr void reserve(size_type new_cap) { 431 | if(new_cap > max_size()) { 432 | ECPP_STATIC_VECTOR_THROW(std::length_error("Reserve exceeds max_size")); 433 | } 434 | } 435 | 436 | /** 437 | * @brief Returns the number of elements that the container has currently allocated space for 438 | * @return Capacity of the currently allocated storage 439 | */ 440 | constexpr size_type capacity() const noexcept { return max_size(); } 441 | 442 | /** 443 | * @brief Requests the removal of unused capacity. 444 | * Because the static_vector uses static storage this method does nothing 445 | */ 446 | constexpr void shrink_to_fit() noexcept { /* Do nothing, as the underlying storage cannot be shrinked */ 447 | } 448 | 449 | /** 450 | * @brief Erases all elements from the container. After this call, size() returns zero. 451 | * Invalidates any references, pointers, or iterators referring to contained elements. Any past-the-end iterators are also invalidated. 452 | * Leaves the capacity() of the vector unchanged 453 | */ 454 | constexpr void clear() noexcept { 455 | std::for_each(begin(), end(), [](reference x) { std::destroy_at(&x); }); 456 | currentSize = 0; 457 | } 458 | 459 | /** 460 | * @brief Inserts value before pos 461 | * @param[in] pos iterator before which the content will be inserted. pos may be the end() iterator 462 | * @param[in] value element value to insert 463 | * @return Iterator pointing to the inserted value 464 | * @note The method will throw LengthError if size() == max_size() 465 | */ 466 | constexpr iterator insert(const_iterator pos, T const &value) { 467 | if(size() + 1 > max_size()) { 468 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 469 | } 470 | auto position = begin() + std::distance(cbegin(), pos); 471 | // Move last element right by one (end() + 1 will become new end(), so uninitialized memory need to be initialized) 472 | if(position != end()) { 473 | std::uninitialized_move(end() - 1, end(), end()); 474 | // Move [pos, end() -1) to [pos + 1, end()) 475 | std::move_backward(position, end() - 1, end()); 476 | } 477 | std::construct_at(position, value); 478 | ++currentSize; 479 | return position; 480 | } 481 | 482 | /** 483 | * @brief Inserts value before pos 484 | * @param[in] pos iterator before which the content will be inserted. pos may be the end() iterator 485 | * @param[in] value element value to insert 486 | * @return Iterator pointing to the inserted value 487 | * @note The method will throw LengthError if size() == max_size() 488 | */ 489 | constexpr iterator insert(const_iterator pos, T &&value) { 490 | if(size() + 1 > max_size()) { 491 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 492 | } 493 | auto position = begin() + std::distance(cbegin(), pos); 494 | // Move last element right by one (end() + 1 will become new end(), so uninitialized memory need to be initialized) 495 | if(position != end()) { 496 | std::uninitialized_move(end() - 1, end(), end()); 497 | // Move [pos, end() -1) to [pos + 1, end()) 498 | std::move_backward(position, end() - 1, end()); 499 | } 500 | std::construct_at(position, std::move(value)); 501 | ++currentSize; 502 | return position; 503 | } 504 | 505 | /** 506 | * @brief Inserts count copies of the value before pos 507 | * @param[in] pos iterator before which the content will be inserted. pos may be the end() iterator 508 | * @param[in] count number of copies to be inserted 509 | * @param[in] value element value to insert 510 | * @return Iterator pointing to the first element inserted, or pos if count==0 511 | * @note The method will throw LengthError if size() + count > max_size() 512 | */ 513 | constexpr iterator insert(const_iterator pos, size_type count, T const &value) { 514 | if(size() + count > max_size()) { 515 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 516 | } 517 | auto position = begin() + std::distance(cbegin(), pos); 518 | if(count > 0) { 519 | if(position != end()) { 520 | auto existingElementsToMove = 521 | std::distance(position, end()); // Negative distance in this context is UB (position must be in range [begin(), end()]) 522 | auto toCopyAtTheEnd = (count >= existingElementsToMove) ? (count - existingElementsToMove) : 0; 523 | auto toMoveAssign = (count >= existingElementsToMove) ? 0 : (existingElementsToMove - count); 524 | 525 | // uninitialized_copy last toCopyAtTheEnd elements of input range at the end(), as they don't overlap with existing data 526 | auto lastElem = std::uninitialized_fill_n(end(), toCopyAtTheEnd, value); 527 | 528 | // Move data from [pos end()) after last element of the vector. If size of the input range is smaller than number of elements to move 529 | std::uninitialized_move(position + toMoveAssign, end(), lastElem); 530 | 531 | std::move_backward(position, position + toMoveAssign, end()); 532 | 533 | std::fill(position, position + count - toCopyAtTheEnd, value); 534 | 535 | } else { 536 | std::uninitialized_fill_n(position, count, value); 537 | } 538 | currentSize += count; 539 | } 540 | return position; 541 | } 542 | 543 | /** 544 | * @brief Inserts elements from range [first, last) before pos 545 | * @param[in] pos iterator before which the content will be inserted. pos may be the end() iterator 546 | * @param[in] first start of the range of elements to insert, can't be iterators into container for which insert is called 547 | * @param[in] last end of the range of elements to insert, can't be iterators into container for which insert is called 548 | * @return Iterator pointing to the first element inserted, or pos if first==last 549 | * @note The method will throw LengthError if size() + count > max_size() 550 | */ 551 | template constexpr iterator insert(const_iterator pos, InputIt first, InputIt last) { 552 | auto dist = std::distance(first, last); 553 | auto count = size_type(dist); 554 | if(dist < 0 || (size() + count > max_size())) { 555 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 556 | } 557 | auto position = begin() + std::distance(cbegin(), pos); 558 | if(position != end()) { 559 | auto existingElementsToMove = 560 | std::distance(position, end()); // Negative distance in this context is UB (position must be in range [begin(), end()]) 561 | auto toCopyAtTheEnd = (dist >= existingElementsToMove) ? (dist - existingElementsToMove) : 0; 562 | auto toMoveAssign = (dist >= existingElementsToMove) ? 0 : (existingElementsToMove - dist); 563 | 564 | // uninitialized_copy last toCopyAtTheEnd elements of input range at the end(), as they don't overlap with existing data 565 | auto lastElem = std::uninitialized_copy(last - toCopyAtTheEnd, last, end()); 566 | 567 | // Move data from [pos end()) after last element of the vector. If size of the input range is smaller than number of elements to move 568 | // 569 | std::uninitialized_move(position + toMoveAssign, end(), lastElem); 570 | 571 | std::move_backward(position, position + toMoveAssign, end()); 572 | 573 | std::copy(first, last - toCopyAtTheEnd, position); 574 | } else { 575 | std::uninitialized_copy(first, last, end()); 576 | } 577 | currentSize += count; 578 | return position; 579 | } 580 | 581 | /** 582 | * @brief Inserts elements from initializer list ilist before pos 583 | * @param[in] pos iterator before which the content will be inserted. pos may be the end() iterator 584 | * @param[in] ilist initializer list to insert the values from 585 | * @return Iterator pointing to the first element inserted, or pos if ilist is empty 586 | * @note The method will throw LengthError if size() + ilist.size() > max_size() 587 | */ 588 | constexpr iterator insert(const_iterator pos, std::initializer_list ilist) { return insert(pos, ilist.begin(), ilist.end()); } 589 | 590 | /** 591 | * @brief Inserts a new element into the container directly before pos 592 | * @param pos iterator before which the new element will be constructed 593 | * @param args arguments to forward to the constructor of the element 594 | * @return Iterator pointing to the emplaced element 595 | * @note The method will throw LengthError if size() == max_size() 596 | */ 597 | template constexpr iterator emplace(const_iterator pos, Args &&...args) { 598 | if(size() + 1 > max_size()) { 599 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 600 | } 601 | auto const position = begin() + std::distance(cbegin(), pos); 602 | // Move last element right by one (end() + 1 will become new end(), so uninitialized memory need to be initialized) 603 | if(position != end()) { 604 | std::uninitialized_move(end() - 1, end(), end()); 605 | // Move [pos, end() -1) to [pos + 1, end()) 606 | std::move_backward(position, end() - 1, end()); 607 | } else { 608 | std::construct_at(position, std::forward(args)...); 609 | } 610 | ++currentSize; 611 | return position; 612 | } 613 | 614 | /** 615 | * @brief Removes the element at pos 616 | * @param[in] pos to the element to remove 617 | * @return Iterator following the last removed element 618 | * @note If pos refers to the last element, then the end() iterator is returned 619 | */ 620 | constexpr iterator erase(const_iterator pos) { 621 | auto index = std::distance(cbegin(), pos); 622 | std::move(begin() + index + 1, end(), begin() + index); 623 | // Elements were moved left, now destroy the last element 624 | currentSize--; 625 | // Now, end() points to previous last element 626 | std::destroy_at(end()); 627 | return begin() + index; 628 | } 629 | 630 | /** 631 | * @brief Removes the elements in the range [first, last) 632 | * @param[in] first start of range of elements to remove 633 | * @param[in] last end of range of elements to remove 634 | * @return Iterator following the last removed element 635 | * @note If last==end() prior to removal, then the updated end() iterator is returned. 636 | * @note If [first, last) is an empty range, then last is returned 637 | */ 638 | constexpr iterator erase(const_iterator first, const_iterator last) { 639 | auto last_ = begin() + std::distance(cbegin(), last); 640 | if(first >= last) 641 | return last_; 642 | auto first_ = begin() + std::distance(cbegin(), first); 643 | 644 | auto toErase = std::distance(first, last); // Guaranteed to be > 0 645 | auto lastValid = std::move(first_ + toErase, end(), first_); 646 | std::for_each(lastValid, end(), [](reference x) { std::destroy_at(&x); }); 647 | 648 | currentSize -= size_type(toErase); 649 | return first_; 650 | } 651 | 652 | /** 653 | * @brief Appends the given element value to the end of the container. The new element is initialized as a copy of value 654 | * @param[in] value the value of the element to append 655 | * @note The method will throw LengthError if size() == max_size() 656 | */ 657 | constexpr void push_back(T const &value) { 658 | if(size() + 1 > max_size()) { 659 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 660 | } 661 | std::construct_at(end(), value); 662 | ++currentSize; 663 | } 664 | 665 | /** 666 | * @brief Appends the given element value to the end of the container; value is moved into the new element 667 | * @param[in] value the value of the element to append 668 | * @note The method will throw LengthError if size() == max_size() 669 | */ 670 | constexpr void push_back(T &&value) { 671 | if(size() + 1 > max_size()) { 672 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 673 | } 674 | std::construct_at(end(), std::move(value)); 675 | ++currentSize; 676 | } 677 | 678 | /** 679 | * @brief Appends a new element to the end of the container. The element is constructed in-place. 680 | * @param[in] args arguments to forward to the constructor of the element 681 | * @return A reference to the inserted element 682 | * @note The method will throw LengthError if size() == max_size() 683 | */ 684 | template constexpr reference emplace_back(Args &&...args) { 685 | if(size() + 1 > max_size()) { 686 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 687 | } 688 | auto const position = end(); 689 | std::construct_at(position, std::forward(args)...); 690 | ++currentSize; 691 | return *position; 692 | } 693 | 694 | /** 695 | * @brief Removes the last element of the container 696 | * Iterators and references to the last element, as well as the end() iterator, are invalidated 697 | * @note Calling pop_back on an empty container results in undefined behavior. 698 | */ 699 | constexpr void pop_back() { 700 | currentSize--; 701 | std::destroy_at(end()); 702 | } 703 | 704 | /** 705 | * @brief Resizes the container to contain count elements 706 | * If the current size is greater than count, the container is reduced to its first count elements. 707 | * If the current size is less than count, additional default-inserted elements are appended 708 | * @param[in] count new size of the container 709 | * @note The method will throw LengthError if count > max_size() 710 | */ 711 | constexpr void resize(size_type count) { 712 | if(count < size()) { 713 | erase(begin() + count, end()); 714 | } else if(count > max_size()) { 715 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 716 | } else { 717 | auto toAdd = count - size(); 718 | for(size_type i = 0; i != toAdd; ++i) { 719 | emplace_back(); 720 | } 721 | } 722 | } 723 | 724 | /** 725 | * @brief Resizes the container to contain count elements 726 | * If the current size is greater than count, the container is reduced to its first count elements. 727 | * If the current size is less than count, additional copies of value are appended. 728 | * @param[in] count new size of the container 729 | * @param[in] value the value to initialize the new elements with 730 | * @note The method will throw LengthError if count > max_size() 731 | */ 732 | constexpr void resize(size_type count, value_type const &value) { 733 | if(count < size()) { 734 | erase(begin() + count, end()); 735 | } else if(count > max_size()) { 736 | ECPP_STATIC_VECTOR_THROW(std::length_error("Insertion would exceed static_vector capacity")); 737 | } else { 738 | auto toAdd = count - size(); 739 | for(size_type i = 0; i != toAdd; ++i) { 740 | push_back(value); 741 | } 742 | } 743 | } 744 | 745 | /** 746 | * Exchanges the contents of the container with those of other. Swaps content of underlying storage. 747 | * All iterators and references are invalidated 748 | * @param[in] other reference to static_vector instance to swap with 749 | */ 750 | constexpr void swap(static_vector &other) noexcept { 751 | container.swap(other.container); 752 | std::swap(currentSize, other.currentSize); 753 | } 754 | }; 755 | 756 | /** 757 | * @brief Checks if the contents of lhs and rhs are equal, that is, they have the same number of elements and each element in lhs compares equal with 758 | * the element in rhs at the same position 759 | * @param[in] lhs first of vectors whose contents to compare 760 | * @param[in] rhs second of vectors whose contents to compare 761 | * @return true if the contents of the vectors are equal, false otherwise 762 | */ 763 | template constexpr bool operator==(static_vector const &lhs, static_vector const &rhs) { 764 | return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); 765 | } 766 | 767 | /** 768 | * @brief Compares the contents of lhs and rhs lexicographically. 769 | * The comparison is performed as if by calling std::lexicographical_compare_three_way on two vectors with a function object performing synthesized 770 | * three-way comparison. The return type is same as the result type of synthesized three-way comparison 771 | * @param[in] lhs first of vectors whose contents to compare 772 | * @param[in] rhs second of vectors whose contents to compare 773 | * @return The relative order of the first pair of non-equivalent elements in lhs and rhs if there are such elements, lhs.size() <=> rhs.size() 774 | * otherwise 775 | */ 776 | template constexpr auto operator<=>(static_vector const &lhs, static_vector const &rhs) { 777 | return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); 778 | } 779 | 780 | /** 781 | * @brief Specializes the std::swap algorithm for static_vector. 782 | * Swaps the contents of lhs and rhs. Calls lhs.swap(rhs). 783 | * @param[in,out] lhs container whose contents to swap 784 | * @param[in,out] rhs containers whose contents to swap 785 | */ 786 | template constexpr void swap(static_vector &lhs, static_vector &rhs) noexcept { lhs.swap(rhs); } 787 | 788 | /** 789 | * @brief Erases all elements that compare equal to value from the container 790 | * @param[in, out] c container from which to erase 791 | * @param[in] value value to be removed 792 | * @return The number of erased elements 793 | */ 794 | template constexpr typename static_vector::size_type erase(static_vector &c, U const &value) { 795 | auto oldSize = c.size(); 796 | auto end = std::remove(c.begin(), c.end(), value); 797 | c.erase(end, c.end()); 798 | return c.size() - oldSize; 799 | } 800 | 801 | /** 802 | * @brief Erases all elements that compare equal to value from the container 803 | * @param[in, out] c container from which to erase 804 | * @param[in] pred unary predicate which returns ​true if the element should be erased 805 | * @return The number of erased elements 806 | */ 807 | template constexpr typename static_vector::size_type erase_if(static_vector &c, Pred pred) { 808 | auto oldSize = c.size(); 809 | auto end = std::remove_if(c.begin(), c.end(), pred); 810 | c.erase(end, c.end()); 811 | return c.size() - oldSize; 812 | } 813 | } // namespace ecpp 814 | 815 | #endif 816 | --------------------------------------------------------------------------------