├── test ├── utils.hpp ├── CMakeLists.txt └── test.cpp ├── ci └── run.sh ├── .gitignore ├── .travis.yml ├── cmake ├── options.cmake ├── packages.cmake ├── format.py ├── tidy.py ├── targets.cmake └── flags.cmake ├── CMakeLists.txt ├── .clang-format ├── .clang-tidy ├── README.md └── include └── experimental └── fixed_capacity_vector /test/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /// \file 3 | /// 4 | /// 5 | #include 6 | #include 7 | -------------------------------------------------------------------------------- /ci/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Configure 4 | mkdir build 5 | cd build 6 | cmake .. 7 | 8 | # Build and run tests 9 | make check -j 4 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | script: cmake 3 | os: linux 4 | dist: trusty 5 | 6 | matrix: 7 | include: 8 | - name: "clang - linux - debug" 9 | - env: CXX=clang++ BUILD_TYPE=Debug 10 | addons: &clang 11 | apt: 12 | packages: ["clang-8.0"] 13 | sources: &sources ["llvm-toolchain-trusty"] 14 | - name: "clang - linux - release" 15 | env: CXX=clang++ BUILD_TYPE=Release 16 | addons: *clang 17 | 18 | script: sh ci/run.sh 19 | 20 | notifications: 21 | email: false 22 | -------------------------------------------------------------------------------- /cmake/options.cmake: -------------------------------------------------------------------------------- 1 | # Copyright Gonzalo Brito Gadeschi 2015 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 4 | # 5 | 6 | include(CMakeDependentOption) 7 | 8 | option(FCVECTOR_ENABLE_ASAN "Run the unit tests and examples using AddressSanitizer." OFF) 9 | option(FCVECTOR_ENABLE_COVERAGE "Run the unit tests and examples with code coverage instrumentation." OFF) 10 | option(FCVECTOR_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF) 11 | option(FCVECTOR_ENABLE_DEBUG_INFORMATION "Includes debug information in the binaries." OFF) 12 | option(FCVECTOR_ENABLE_ASSERTIONS "Enables assertions." ON) 13 | option(FCVECTOR_ENABLE_LIKELY "Enables branch-prediction hints (FCVECTOR_LIKELY/FCVECTOR_UNLIKELY macros)." ON) 14 | -------------------------------------------------------------------------------- /cmake/packages.cmake: -------------------------------------------------------------------------------- 1 | # Copyright Gonzalo Brito Gadeschi 2015 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 4 | 5 | # Git: parses current project commit 6 | find_package(Git) 7 | if (GIT_FOUND) 8 | execute_process( 9 | COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD 10 | OUTPUT_VARIABLE STACK_VECTOR_CURRENT_COMMIT 11 | OUTPUT_STRIP_TRAILING_WHITESPACE 12 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 13 | endif() 14 | 15 | # Doxygen 16 | find_package(Doxygen) 17 | 18 | # Valgrind 19 | find_program(MEMORYCHECK_COMMAND valgrind) 20 | if(MEMORYCHECK_COMMAND-NOTFOUND) 21 | message(FATAL_ERROR "valgrind not found") 22 | else() 23 | set(MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full") 24 | include(Dart) 25 | endif() 26 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Gonzalo Brito Gadeschi 2015 2 | 3 | # Setup project 4 | project(static_vector CXX) 5 | cmake_minimum_required(VERSION 3.0) 6 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export compilation data-base 8 | 9 | # Build system 10 | include(options) 11 | include(flags) 12 | include(targets) 13 | include(packages) 14 | 15 | # Include library 16 | include_directories(${static_vector_SOURCE_DIR}/include) 17 | enable_testing() 18 | 19 | # Setup subdirectories 20 | add_subdirectory(test) 21 | 22 | 23 | # Setup the `check` target to build, check format, and then run all the tests 24 | # and examples. 25 | add_custom_target(check ALL 26 | COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure 27 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 28 | DEPENDS tests test.headers 29 | COMMENT "Build and then run all the tests and examples.") 30 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AccessModifierOffset: -2 3 | AlignAfterOpenBracket: true 4 | AlignConsecutiveAssignments: true 5 | AlignEscapedNewlinesLeft: true 6 | AlignOperands: true 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: false 9 | AllowShortBlocksOnASingleLine: false 10 | AllowShortCaseLabelsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: false 12 | AllowShortIfStatementsOnASingleLine: false 13 | AllowShortLoopsOnASingleLine: false 14 | AlwaysBreakAfterReturnType: None 15 | AlwaysBreakBeforeMultilineStrings: false 16 | AlwaysBreakTemplateDeclarations: true 17 | BinPackArguments: true 18 | BinPackParameters: true 19 | BreakBeforeBinaryOperators: All 20 | BreakBeforeBraces: Custom 21 | BraceWrapping: 22 | AfterClass: true 23 | AfterControlStatement: true 24 | AfterEnum: true 25 | AfterFunction: true 26 | AfterNamespace: true 27 | AfterObjCDeclaration: true 28 | AfterStruct: true 29 | AfterUnion: true 30 | BeforeCatch: true 31 | BeforeElse: true 32 | IndentBraces: false 33 | BreakBeforeInheritanceComma: true 34 | BreakBeforeTernaryOperators: true 35 | BreakConstructorInitializersBeforeComma: true 36 | ColumnLimit: 80 37 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 38 | ConstructorInitializerIndentWidth: 4 39 | ContinuationIndentWidth: 4 40 | Cpp11BracedListStyle: true 41 | DerivePointerBinding: false 42 | ExperimentalAutoDetectBinPacking: false 43 | FixNamespaceComments: true 44 | ForEachMacros: ['RANGES_FOR', 'BOOST_FOREACH', 'FOREACH'] 45 | IndentCaseLabels: true 46 | IndentWidth: 4 47 | IndentWrappedFunctionNames: false 48 | KeepEmptyLinesAtTheStartOfBlocks: false 49 | Language: Cpp 50 | MaxEmptyLinesToKeep: 1 51 | NamespaceIndentation: All 52 | PointerAlignment: Left 53 | ReflowComments: true 54 | SortIncludes: true 55 | SpaceAfterCStyleCast: false 56 | SpaceBeforeAssignmentOperators: true 57 | SpaceBeforeParens: ControlStatements 58 | SpaceInEmptyParentheses: false 59 | SpacesBeforeTrailingComments: 2 60 | SpacesInAngles: false 61 | SpacesInCStyleCastParentheses: false 62 | SpacesInContainerLiterals: false 63 | SpacesInParentheses: false 64 | SpacesInSquareBrackets: false 65 | Standard: Cpp11 66 | TabWidth: 0 67 | UseTab: Never 68 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Louis Dionne 2015 2 | # Copyright Gonzalo Brito Gadeschi 2015 3 | # Distributed under the Boost Software License, Version 1.0. 4 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 5 | 6 | # Setup custom functions, master targets and file lists for the unit tests 7 | add_custom_target(test.headers 8 | COMMENT "Build all the header-inclusion unit tests.") 9 | 10 | add_custom_target(tests 11 | DEPENDS test.headers 12 | COMMENT "Build all the unit tests.") 13 | 14 | # fcvector_add_unit_test( ...) 15 | # 16 | # Equivalent to `fcvector_add_test`, except the test is also added as a 17 | # dependency of the `tests` target. 18 | function(fcvector_add_unit_test name) 19 | add_test(${name} ${ARGN}) 20 | fcvector_add_packages_to_target(${name}) 21 | add_dependencies(tests ${name}) 22 | endfunction() 23 | 24 | # fcvector_add_header_test() 25 | # 26 | # Add a unit test for the public header file `header-name`, which must be a 27 | # relative path from fcvector's include directory, e.g. `fcvector/fcvector.hpp`. 28 | # 29 | # This function creates an executable named `header.header-name` and a test 30 | # of the same name. The only source file of the executable contains an empty 31 | # `main` and it includes the `header-name` file. This is used to make sure 32 | # that including any public header works properly. Also, the 33 | # `header.header-name` target is made a dependency of the `headers` target. 34 | function(fcvector_add_header_test header) 35 | string(REGEX REPLACE "/" "." _target "${header}") 36 | if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/header/${header}.cpp") 37 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/header/${header}.cpp" " 38 | #include <${header}> 39 | int main() { return 0; } 40 | ") 41 | endif() 42 | add_executable(test.header.${_target} EXCLUDE_FROM_ALL 43 | "${CMAKE_CURRENT_BINARY_DIR}/header/${header}.cpp") 44 | fcvector_add_packages_to_target(test.header.${_target}) 45 | add_dependencies(test.headers test.header.${_target}) 46 | endfunction() 47 | 48 | # A list of all the test files 49 | file(GLOB_RECURSE FCVECTOR_TEST_SOURCES "${fcvector_SOURCE_DIR}/test/*.cpp") 50 | 51 | # A list of all the public headers 52 | file(GLOB_RECURSE FCVECTOR_PUBLIC_HEADERS "${fcvector_SOURCE_DIR}/include/*.hpp") 53 | 54 | # Generate tests that include each public header 55 | foreach(_header IN LISTS FCVECTOR_PUBLIC_HEADERS) 56 | file(RELATIVE_PATH _relative "${fcvector_SOURCE_DIR}/include" "${_header}") 57 | fcvector_add_header_test("${_relative}") 58 | endforeach() 59 | 60 | # Add all the unit tests 61 | foreach(_file IN LISTS FCVECTOR_TEST_SOURCES) 62 | file(READ "${_file}" _contents) 63 | fcvector_target_name_for(_target "${_file}") 64 | 65 | add_executable(${_target} EXCLUDE_FROM_ALL "${_file}") 66 | fcvector_add_unit_test(${_target} ${CMAKE_CURRENT_BINARY_DIR}/${_target}) 67 | endforeach() 68 | -------------------------------------------------------------------------------- /cmake/format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Gonzalo Brito Gadeschi 2015 3 | # Distributed under the Boost Software License, Version 1.0. 4 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 5 | """Recursively formats C/C++ files using clang-format 6 | 7 | Usage: 8 | format.py [options] 9 | format.py -h | --help 10 | format.py --version 11 | 12 | Path to clang-format's binary. 13 | Path to the project's source directory. 14 | 15 | Options: 16 | -h --help Show this screen. 17 | --verbose Verbose output. 18 | --apply Applies format (by default it only checks the format). 19 | 20 | """ 21 | from docopt import docopt 22 | import os 23 | import subprocess 24 | 25 | file_extensions = ['.c', '.h', '.cpp', '.cc', '.cxx', 26 | '.hpp', '.hh', '.hxx', '.c++', '.h++'] 27 | 28 | def run_clang_format(clang_format, path, apply_format, verbose): 29 | if apply_format: 30 | cmd = clang_format + ' -style=file -i ' + path 31 | else: 32 | cmd = clang_format + ' -style=file ' + path + ' | diff -u ' + path + ' - ' 33 | 34 | if verbose: print('[clang_format cmd]: "{0}"'.format(cmd)) 35 | 36 | p = subprocess.Popen(cmd, universal_newlines=False, shell=True, 37 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 38 | 39 | out, err = p.communicate() 40 | if len(out) > 0: 41 | print out 42 | if len(err) > 0: 43 | print err 44 | 45 | if not p.returncode == 0 or len(err) > 0 or len(out) > 0: 46 | if verbose: print("failed!") 47 | return False 48 | else: 49 | if verbose: print("success!") 50 | return True 51 | 52 | def run(clang_format_path, file_paths, apply_format, verbose): 53 | result = True 54 | for p in file_paths: 55 | _, ext = os.path.splitext(p) 56 | if ext in file_extensions: 57 | r = run_clang_format(clang_format_path, p, apply_format, verbose) 58 | if not r: 59 | result = False 60 | return result 61 | 62 | def main(): 63 | args = docopt(__doc__) 64 | 65 | clang_format_path = args[''] 66 | project_src_path = args[''] 67 | verbose = args['--verbose'] 68 | 69 | files = subprocess.check_output(['git', 'ls-tree', '--full-tree', '-r', 'HEAD', project_src_path]) 70 | file_paths = [os.path.join(project_src_path,f.split('\t')[1]) for f in files.splitlines()] 71 | 72 | apply_format = args['--apply'] 73 | 74 | result = run(clang_format_path, file_paths, apply_format, verbose) 75 | 76 | if apply_format: # If format was applied: check format and use that as result 77 | result = run(clang_format_path, file_paths, False, verbose) 78 | 79 | if verbose: 80 | if result: 81 | print("finished with success!") 82 | else: 83 | print("finished with failed!") 84 | if result: 85 | exit(0) 86 | else: 87 | exit(1) 88 | 89 | 90 | if __name__ == '__main__': 91 | main() 92 | -------------------------------------------------------------------------------- /cmake/tidy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright Gonzalo Brito Gadeschi 2015 3 | # Distributed under the Boost Software License, Version 1.0. 4 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 5 | """Recursively tidies C/C++ files using clang-tidy 6 | 7 | Usage: 8 | tidy.py [options] 9 | tidy.py -h | --help 10 | tidy.py --version 11 | 12 | Path to clang-tidy's binary. 13 | Path to the project's source directory. 14 | Path to the project's build directory. 15 | 16 | Options: 17 | -h --help Show this screen. 18 | --verbose Verbose output. 19 | --apply Applies tidy (by default it only checks the tidy). 20 | 21 | """ 22 | from docopt import docopt 23 | import os 24 | import subprocess 25 | 26 | file_extensions = ['.c', '.cpp', '.cc', '.cxx', '.c++'] 27 | 28 | def run_clang_tidy(clang_tidy, path, build_path, apply_tidy, verbose, supp): 29 | 30 | if apply_tidy: 31 | cmd = clang_tidy + ' -p=' + build_path + ' -fix ' + ' -line-filter=' + supp + ' ' + path 32 | else: 33 | cmd = clang_tidy + ' -p=' + build_path + ' -line-filter=' + supp + ' -header-filter="\S*(stack_vector)+\S*"' + ' ' + path #+ ' -dump-config' 34 | 35 | if verbose: print('[clang_tidy cmd]: "{0}"'.format(cmd)) 36 | 37 | p = subprocess.Popen(cmd, universal_newlines=False, shell=True, 38 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 39 | 40 | out, err = p.communicate() 41 | if len(out) > 0: 42 | print out 43 | if len(err) > 0: 44 | print err 45 | 46 | return p.returncode == 0 47 | 48 | def run(clang_tidy_path, file_paths, build_path, apply_tidy, verbose, supp): 49 | result = True 50 | for p in file_paths: 51 | _, ext = os.path.splitext(p) 52 | if ext in file_extensions: 53 | r = run_clang_tidy(clang_tidy_path, p, build_path, apply_tidy, verbose, supp) 54 | if not r: 55 | result = False 56 | return result 57 | 58 | 59 | def main(): 60 | args = docopt(__doc__) 61 | 62 | clang_tidy_path = args[''] 63 | project_src_path = args[''] 64 | project_build_path = args[''] 65 | verbose = args['--verbose'] 66 | 67 | apply_tidy = args['--apply'] 68 | 69 | files = subprocess.check_output(['git', 'ls-tree', '--full-tree', '-r', 'HEAD', project_src_path]) 70 | file_paths = [os.path.join(project_src_path,f.split('\t')[1]) for f in files.splitlines()] 71 | 72 | supp_file_path = os.path.join(project_src_path, '.clang-tidy.supp') 73 | if os.path.exists(supp_file_path): 74 | with file(supp_file_path, 'r') as supp_file: 75 | supp = supp_file.read() 76 | supp = supp.replace('src_path', project_src_path) 77 | supp = '"' + supp.replace('\n','').replace('"', '\\"') + '"' 78 | else: 79 | supp = '""' 80 | 81 | result = run(clang_tidy_path, file_paths, project_build_path, apply_tidy, verbose, supp) 82 | 83 | exit(result) 84 | 85 | if __name__ == '__main__': 86 | main() 87 | -------------------------------------------------------------------------------- /cmake/targets.cmake: -------------------------------------------------------------------------------- 1 | # Copyright Louis Dionne 2015 2 | # Copyright Gonzalo Brito Gadeschi 2015 3 | # Distributed under the Boost Software License, Version 1.0. 4 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 5 | 6 | ############################################################################## 7 | # Setup custom functions to ease the creation of targets 8 | ############################################################################## 9 | # fcvector_target_name_for( [ext]) 10 | # 11 | # Return the target name associated to a source file. If the path of the 12 | # source file relative from the root of fcvector is `path/to/source/file.ext`, 13 | # the target name associated to it will be `path.to.source.file`. 14 | # 15 | # The extension of the file should be specified as a last argument. If no 16 | # extension is specified, the `.cpp` extension is assumed. 17 | function(fcvector_target_name_for out file) 18 | if (NOT ARGV2) 19 | set(_extension ".cpp") 20 | else() 21 | set(_extension "${ARGV2}") 22 | endif() 23 | 24 | file(RELATIVE_PATH _relative ${fcvector_SOURCE_DIR} ${file}) 25 | string(REPLACE "${_extension}" "" _name ${_relative}) 26 | string(REGEX REPLACE "/" "." _name ${_name}) 27 | set(${out} "${_name}" PARENT_SCOPE) 28 | endfunction() 29 | 30 | # fcvector_list_remove_glob( [globbing expressions]...) 31 | # 32 | # Generates a list of files matching the given glob expressions, and remove 33 | # the matched elements from the given . 34 | macro(fcvector_list_remove_glob list glob) 35 | file(${glob} _bhlrg10321023_avoid_macro_clash_matches ${ARGN}) 36 | list(REMOVE_ITEM ${list} ${_bhlrg10321023_avoid_macro_clash_matches}) 37 | endmacro() 38 | 39 | # fcvector_add_packages_to_target( [...]) 40 | # 41 | # Adds packages as a dependency to target 42 | function(fcvector_add_packages_to_target name) 43 | add_dependencies(${name} fetch_packages) 44 | endfunction() 45 | 46 | # The `format` target re-formats/checks all the c++ files in the git repo. 47 | # 48 | # Two targets are added: 49 | # - make format: reformats all files in the git repo. 50 | # - make check-format: check the format of all files in the git repo. 51 | find_program(CLANG_FORMAT NAMES clang-format-4.0 clang-format-3.9 clang-format-3.8 clang-format-3.7 clang-format-3.6 clang-format) 52 | if(CLANG_FORMAT) 53 | add_custom_command(OUTPUT format-cmd COMMAND 54 | ${PROJECT_SOURCE_DIR}/cmake/format.py ${CLANG_FORMAT} ${PROJECT_SOURCE_DIR} --apply) 55 | add_custom_target(format DEPENDS format-cmd WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) 56 | 57 | add_custom_command(OUTPUT check-format-cmd COMMAND 58 | ${PROJECT_SOURCE_DIR}/cmake/format.py ${CLANG_FORMAT} ${PROJECT_SOURCE_DIR}) 59 | add_custom_target(check-format DEPENDS check-format-cmd WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) 60 | endif() 61 | 62 | # The `check-tidy` target checks all the c++ files in the git repo. 63 | # 64 | # One target is added: 65 | # - make check-tidy 66 | find_program(CLANG_TIDY NAMES clang-tidy-4.0 clang-tidy-3.9 clang-tidy-3.8 clang-tidy-3.7 clang-tidy-3.6 clang-tidy) 67 | if(CLANG_TIDY) 68 | add_custom_command(OUTPUT check-tidy-cmd COMMAND 69 | ${PROJECT_SOURCE_DIR}/cmake/tidy.py ${CLANG_TIDY} ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} --verbose) 70 | add_custom_target(check-tidy DEPENDS check-tidy-cmd fetch_packages check-format WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) 71 | endif() 72 | 73 | # Target for fetching packages 74 | add_custom_target(fetch_packages) 75 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-llvm-include-order' 3 | HeaderFilterRegex: '.*' 4 | AnalyzeTemporaryDtors: true 5 | CheckOptions: 6 | - key: readability-identifier-naming.AbstractClassCase 7 | value: lower_case 8 | - key: readability-identifier-naming.ClassCase 9 | value: lower_case 10 | - key: readability-identifier-naming.ClassConstantCase 11 | value: lower_case 12 | - key: readability-identifier-naming.ClassMemberCase 13 | value: lower_case 14 | - key: readability-identifier-naming.ClassMethodCase 15 | value: lower_case 16 | - key: readability-identifier-naming.ConstantCase 17 | value: lower_case 18 | - key: readability-identifier-naming.ConstantMemberCase 19 | value: lower_case 20 | - key: readability-identifier-naming.ConstantParameterCase 21 | value: lower_case 22 | - key: readability-identifier-naming.ConstexprFunctionCase 23 | value: lower_case 24 | - key: readability-identifier-naming.ConstexprMethodCase 25 | value: lower_case 26 | - key: readability-identifier-naming.ConstexprVariableCase 27 | value: lower_case 28 | - key: readability-identifier-naming.EnumCase 29 | value: lower_case 30 | - key: readability-identifier-naming.EnumConstantCase 31 | value: lower_case 32 | - key: readability-identifier-naming.FunctionCase 33 | value: lower_case 34 | - key: readability-identifier-naming.GlobalConstantCase 35 | value: lower_case 36 | - key: readability-identifier-naming.GlobalFunctionCase 37 | value: lower_case 38 | - key: readability-identifier-naming.GlobalVariableCase 39 | value: lower_case 40 | - key: readability-identifier-naming.IgnoreFailedSplit 41 | value: '0' 42 | - key: readability-identifier-naming.InlineNamespaceCase 43 | value: lower_case 44 | - key: readability-identifier-naming.LocalConstantCase 45 | value: lower_case 46 | - key: readability-identifier-naming.LocalVariableCase 47 | value: lower_case 48 | - key: readability-identifier-naming.MemberCase 49 | value: lower_case 50 | - key: readability-identifier-naming.MethodCase 51 | value: lower_case 52 | - key: readability-identifier-naming.NamespaceCase 53 | value: lower_case 54 | - key: readability-identifier-naming.ParameterCase 55 | value: lower_case 56 | - key: readability-identifier-naming.ParameterPackCase 57 | value: lower_case 58 | - key: readability-identifier-naming.PrivateMemberCase 59 | value: lower_case 60 | - key: readability-identifier-naming.PrivateMemberSuffix 61 | value: '_' 62 | - key: readability-identifier-naming.PrivateMethodCase 63 | value: lower_case 64 | - key: readability-identifier-naming.PrivateMethodSuffix 65 | value: '' 66 | - key: readability-identifier-naming.ProtectedMemberCase 67 | value: lower_case 68 | - key: readability-identifier-naming.ProtectedMemberSuffix 69 | value: '' 70 | - key: readability-identifier-naming.ProtectedMethodCase 71 | value: lower_case 72 | - key: readability-identifier-naming.PublicMemberCase 73 | value: lower_case 74 | - key: readability-identifier-naming.PublicMemberSuffix 75 | value: '' 76 | - key: readability-identifier-naming.PublicMethodCase 77 | value: lower_case 78 | - key: readability-identifier-naming.StaticConstantCase 79 | value: lower_case 80 | - key: readability-identifier-naming.StaticVariableCase 81 | value: lower_case 82 | - key: readability-identifier-naming.StructCase 83 | value: lower_case 84 | - key: readability-identifier-naming.TemplateParameterCase 85 | value: CamelCase 86 | - key: readability-identifier-naming.TemplateTemplateParameterCase 87 | value: CamelCase 88 | - key: readability-identifier-naming.TypeTemplateParameterCase 89 | value: CamelCase 90 | - key: readability-identifier-naming.TypedefCase 91 | value: any_case 92 | - key: readability-identifier-naming.UnionCase 93 | value: lower_case 94 | - key: readability-identifier-naming.ValueTemplateParameterCase 95 | value: CamelCase 96 | - key: readability-identifier-naming.VariableCase 97 | value: lower_case 98 | - key: readability-identifier-naming.VirtualMethodCase 99 | value: lower_case 100 | -------------------------------------------------------------------------------- /cmake/flags.cmake: -------------------------------------------------------------------------------- 1 | # Copyright Louis Dionne 2015 2 | # Copyright Gonzalo Brito Gadeschi 2015 3 | # Distributed under the Boost Software License, Version 1.0. 4 | # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 5 | # 6 | # Setup compiler flags. 7 | 8 | include(CheckCXXCompilerFlag) 9 | 10 | # Macro used to append only those flags supported by the compiler: 11 | macro(fcvector_append_flag testname flag) 12 | check_cxx_compiler_flag(${flag} ${testname}) 13 | if (${testname}) 14 | add_compile_options(${flag}) 15 | endif() 16 | endmacro() 17 | 18 | # Language flag: version of the C++ standard to use 19 | fcvector_append_flag(FCVECTOR_HAS_STDCXX1Z -std=c++1z) 20 | 21 | # Enable all warnings and make them errors: 22 | fcvector_append_flag(FCVECTOR_HAS_WERROR -Werror) 23 | fcvector_append_flag(FCVECTOR_HAS_WX -WX) 24 | fcvector_append_flag(FCVECTOR_HAS_WALL -Wall) 25 | fcvector_append_flag(FCVECTOR_HAS_WEXTRA -Wextra) 26 | fcvector_append_flag(FCVECTOR_HAS_ZA -Za) 27 | fcvector_append_flag(FCVECTOR_HAS_WEVERYTHING -Weverything) 28 | fcvector_append_flag(FCVECTOR_HAS_PEDANTIC -pedantic) 29 | fcvector_append_flag(FCVECTOR_HAS_PEDANTIC_ERRORS -pedantic-errors) 30 | 31 | # Selectively disable warnings with too many falses: 32 | fcvector_append_flag(FCVECTOR_HAS_WNO_CXX98_COMPAT -Wno-c++98-compat) 33 | fcvector_append_flag(FCVECTOR_HAS_WNO_CXX98_COMPAT_PEDANTIC -Wno-c++98-compat-pedantic) 34 | fcvector_append_flag(FCVECTOR_HAS_WNO_PADDED -Wno-padded) 35 | fcvector_append_flag(FCVECTOR_HAS_WNO_WEAK_VTABLES -Wno-weak-vtables) 36 | 37 | if (FCVECTOR_ENV_MACOSX) 38 | fcvector_append_flag(FCVECTOR_HAS_WNO_GLOBAL_CONSTRUCTORS -Wno-global-constructors) 39 | fcvector_append_flag(FCVECTOR_HAS_WNO_EXIT_TIME_DESTRUCTORS -Wno-exit-time-destructors) 40 | endif() 41 | 42 | if (FCVECTOR_CXX_COMPILER_GCC) 43 | if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.0") 44 | fcvector_append_flag(FCVECTOR_HAS_WNO_STRICT_OVERFLOW -Wno-strict-overflow) 45 | if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0") 46 | fcvector_append_flag(FCVECTOR_HAS_WNO_MISSING_FIELD_INITIALIZERS -Wno-missing-field-initializers) 47 | endif() 48 | endif() 49 | endif() 50 | 51 | if (FCVECTOR_ENV_LINUX AND FCVECTOR_CXX_COMPILER_CLANG) 52 | # On linux libc++ re-exports the system math headers. The ones from libstdc++ 53 | # use the GCC __extern_always_inline intrinsic which is not supported by clang 54 | # versions 3.6, 3.7, 3.8, 3.9, 4.0, and current trunk 5.0 (as of 2017.04.13). 55 | # 56 | # This works around it by replacing __extern_always_inline with inline using a 57 | # macro: 58 | fcvector_append_flag(FCVECTOR_HAS_D__EXTERN_ALWAYS_INLINE -D__extern_always_inline=inline) 59 | endif() 60 | 61 | # Template diagnostic flags 62 | fcvector_append_flag(FCVECTOR_HAS_FDIAGNOSTIC_SHOW_TEMPLATE_TREE -fdiagnostics-show-template-tree) 63 | fcvector_append_flag(FCVECTOR_HAS_FTEMPLATE_BACKTRACE_LIMIT "-ftemplate-backtrace-limit=0") 64 | 65 | # Clang modules support 66 | if (FCVECTOR_MODULES) 67 | fcvector_append_flag(FCVECTOR_HAS_MODULES -fmodules) 68 | fcvector_append_flag(FCVECTOR_HAS_MODULE_MAP_FILE "-fmodule-map-file=${PROJECT_SOURCE_DIR}/include/module.modulemap") 69 | fcvector_append_flag(FCVECTOR_HAS_MODULE_CACHE_PATH "-fmodules-cache-path=${PROJECT_BINARY_DIR}/module.cache") 70 | if (FCVECTOR_LIBCXX_MODULE) 71 | fcvector_append_flag(FCVECTOR_HAS_LIBCXX_MODULE_MAP_FILE "-fmodule-map-file=${FCVECTOR_LIBCXX_MODULE}") 72 | endif() 73 | if (FCVECTOR_ENV_MACOSX) 74 | fcvector_append_flag(FCVECTOR_HAS_NO_IMPLICIT_MODULE_MAPS -fno-implicit-module-maps) 75 | endif() 76 | if (FCVECTOR_DEBUG_BUILD) 77 | fcvector_append_flag(FCVECTOR_HAS_GMODULES -gmodules) 78 | endif() 79 | endif() 80 | 81 | # Sanitizer support: detect incompatible sanitizer combinations 82 | if (FCVECTOR_ASAN AND FCVECTOR_MSAN) 83 | message(FATAL_ERROR "[fcvector error]: AddressSanitizer and MemorySanitizer are both enabled at the same time!") 84 | endif() 85 | 86 | if (FCVECTOR_MSAN AND FCVECTOR_ENV_MACOSX) 87 | message(FATAL_ERROR "[fcvector error]: MemorySanitizer is not supported on MacOSX!") 88 | endif() 89 | 90 | # AddressSanitizer support 91 | if (FCVECTOR_ASAN) 92 | # This policy enables passing the linker flags to the linker when trying to 93 | # test the features, which is required to successfully link ASan binaries 94 | cmake_policy(SET CMP0056 NEW) 95 | set (ASAN_FLAGS "") 96 | if (FCVECTOR_ENV_MACOSX) # LeakSanitizer not supported on MacOSX 97 | set (ASAN_FLAGS "-fsanitize=address,integer,undefined,nullability") 98 | else() 99 | if (FCVECTOR_CXX_COMPILER_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.0") 100 | set (ASAN_FLAGS "-fsanitize=address") 101 | else() 102 | set (ASAN_FLAGS "-fsanitize=address,integer,undefined,leak,nullability") 103 | endif() 104 | endif() 105 | fcvector_append_flag(FCVECTOR_HAS_ASAN "${ASAN_FLAGS}") 106 | if (FCVECTOR_HAS_ASAN) #ASAN flags must be passed to the linker: 107 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${ASAN_FLAGS}") 108 | endif() 109 | fcvector_append_flag(FCVECTOR_HAS_SANITIZE_NO_RECOVER "-fno-sanitize-recover=all") 110 | fcvector_append_flag(FCVECTOR_HAS_NO_OMIT_FRAME_POINTER -fno-omit-frame-pointer) 111 | endif() 112 | 113 | # MemorySanitizer support 114 | if (FCVECTOR_MSAN) 115 | # This policy enables passing the linker flags to the linker when trying to 116 | # compile the examples, which is required to successfully link MSan binaries 117 | cmake_policy(SET CMP0056 NEW) 118 | fcvector_append_flag(FCVECTOR_HAS_MSAN "-fsanitize=memory") 119 | fcvector_append_flag(FCVECTOR_HAS_MSAN_TRACK_ORIGINS -fsanitize-memory-track-origins) 120 | fcvector_append_flag(FCVECTOR_HAS_SANITIZE_RECOVER_ALL "-fno-sanitize-recover=all") 121 | fcvector_append_flag(FCVECTOR_HAS_NO_OMIT_FRAME_POINTER -fno-omit-frame-pointer) 122 | endif() 123 | 124 | # Build types: 125 | if (FCVECTOR_DEBUG_BUILD AND FCVECTOR_RELEASE_BUILD) 126 | message(FATAL_ERROR "[fcvector error] Cannot simultaneously generate debug and release builds!") 127 | endif() 128 | 129 | if (FCVECTOR_DEBUG_BUILD) 130 | fcvector_append_flag(FCVECTOR_HAS_O0 -O0) 131 | fcvector_append_flag(FCVECTOR_HAS_NO_INLINE -fno-inline) 132 | fcvector_append_flag(FCVECTOR_HAS_STACK_PROTECTOR_ALL -fstack-protector-all) 133 | fcvector_append_flag(FCVECTOR_HAS_NO_STRICT_ALIASING -fno-strict-aliasing) 134 | fcvector_append_flag(FCVECTOR_HAS_G3 -g3) 135 | # Clang can generate debug info tuned for LLDB or GDB 136 | if (FCVECTOR_CXX_COMPILER_CLANG) 137 | if (FCVECTOR_ENV_MACOSX) 138 | fcvector_append_flag(FCVECTOR_HAS_GLLDB -glldb) 139 | elseif(FCVECTOR_ENV_LINUX) 140 | fcvector_append_flag(FCVECTOR_HAS_GGDB -ggdb) 141 | endif() 142 | endif() 143 | endif() 144 | 145 | if (FCVECTOR_RELEASE_BUILD) 146 | if (NOT FCVECTOR_ASSERTIONS) 147 | fcvector_append_flag(FCVECTOR_HAS_DNDEBUG -DNDEBUG) 148 | endif() 149 | if (NOT FCVECTOR_ASAN AND NOT FCVECTOR_MSAN) 150 | # The quality of ASan and MSan error messages suffers if we disable the 151 | # frame pointer, so leave it enabled when compiling with either of them: 152 | fcvector_append_flag(FCVECTOR_HAS_OMIT_FRAME_POINTER -fomit-frame-pointer) 153 | endif() 154 | 155 | fcvector_append_flag(FCVECTOR_HAS_OFAST -Ofast) 156 | fcvector_append_flag(FCVECTOR_HAS_STRICT_ALIASING -fstrict-aliasing) 157 | if (NOT FCVECTOR_CXX_COMPILER_CLANGC2) 158 | fcvector_append_flag(FCVECTOR_HAS_STRICT_VTABLE_POINTERS -fstrict-vtable-pointers) 159 | endif() 160 | fcvector_append_flag(FCVECTOR_HAS_FAST_MATH -ffast-math) 161 | fcvector_append_flag(FCVECTOR_HAS_VECTORIZE -fvectorize) 162 | 163 | if (NOT FCVECTOR_ENV_MACOSX) 164 | # Sized deallocation is not available in MacOSX: 165 | fcvector_append_flag(FCVECTOR_HAS_SIZED_DEALLOCATION -fsized-deallocation) 166 | endif() 167 | 168 | if (FCVECTOR_LLVM_POLLY) 169 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly -mllvm -polly-vectorizer=stripmine") 170 | endif() 171 | 172 | if (FCVECTOR_CXX_COMPILER_CLANG AND (NOT (FCVECTOR_INLINE_THRESHOLD EQUAL -1))) 173 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -inline-threshold=${FCVECTOR_INLINE_THRESHOLD}") 174 | endif() 175 | endif() 176 | 177 | if (FCVECTOR_NATIVE) 178 | fcvector_append_flag(FCVECTOR_HAS_MARCH_NATIVE "-march=native") 179 | fcvector_append_flag(FCVECTOR_HAS_MTUNE_NATIVE "-mtune=native") 180 | endif() 181 | 182 | if (FCVECTOR_VERBOSE_BUILD) 183 | message("[fcvector]: C++ flags: ${CMAKE_CXX_FLAGS}") 184 | message("[fcvector]: C++ debug flags: ${CMAKE_CXX_FLAGS_DEBUG}") 185 | message("[fcvector]: C++ Release Flags: ${CMAKE_CXX_FLAGS_RELEASE}") 186 | message("[fcvector]: C++ Compile Flags: ${CMAKE_CXX_COMPILE_FLAGS}") 187 | message("[fcvector]: Compile options: ${COMPILE_OPTIONS_}") 188 | message("[fcvector]: C Flags: ${CMAKE_C_FLAGS}") 189 | message("[fcvector]: C Compile Flags: ${CMAKE_C_COMPILE_FLAGS}") 190 | message("[fcvector]: EXE Linker flags: ${CMAKE_EXE_LINKER_FLAGS}") 191 | message("[fcvector]: C++ Linker flags: ${CMAKE_CXX_LINK_FLAGS}") 192 | message("[fcvector]: MODULE Linker flags: ${CMAKE_MODULE_LINKER_FLAGS}") 193 | get_directory_property(CMakeCompDirDefs COMPILE_DEFINITIONS) 194 | message("[fcvector]: Compile Definitions: ${CmakeCompDirDefs}") 195 | endif() 196 | 197 | 198 | # Template diagnostic flags 199 | fcvector_append_flag(FCVECTOR_HAS_FDIAGNOSTIC_SHOW_TEMPLATE_TREE -fdiagnostics-show-template-tree) 200 | fcvector_append_flag(FCVECTOR_HAS_FTEMPLATE_BACKTRACE_LIMIT "-ftemplate-backtrace-limit=0") 201 | fcvector_append_flag(FCVECTOR_HAS___EXTERN_ALWAYS_INLINE -D__extern_always_inline=inline) 202 | 203 | 204 | if (FCVECTOR_ENABLE_ASAN) 205 | set(FCVECTOR_ASAN_FLAGS "-fsanitize=address,integer,undefined -fno-sanitize-recover=address,integer,undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/sanitize.blacklist") 206 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FCVECTOR_ASAN_FLAGS}") 207 | fcvector_append_flag(FCVECTOR_HAS_NO_OMIT_FRAME_POINTER -fno-omit-frame-pointer) 208 | else() 209 | fcvector_append_flag(FCVECTOR_HAS_OMIT_FRAME_POINTER -fomit-frame-pointer) 210 | endif() 211 | 212 | if (FCVECTOR_ENABLE_DEBUG_INFORMATION) 213 | fcvector_append_flag(FCVECTOR_HAS_G3 -g3) 214 | else() 215 | fcvector_append_flag(FCVECTOR_HAS_G0 -g0) 216 | endif() 217 | 218 | if (NOT FCVECTOR_ENABLE_ASSERTIONS) 219 | fcvector_append_flag(FCVECTOR_HAS_DFCVECTOR_DISABLE_ASSERTIONS -DFCVECTOR_DISABLE_ASSERTIONS) 220 | endif() 221 | 222 | if (FCVECTOR_ENABLE_COVERAGE) 223 | if (CMAKE_BUILD_TYPE STREQUAL "Release") 224 | message(FATAL_ERROR "code coverage instrumentation requires CMAKE_BUILD_TYPE=Debug") 225 | endif() 226 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") 227 | endif() 228 | 229 | # Optimization flags: debug 230 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 231 | fcvector_append_flag(FCVECTOR_HAS_O0 -O0) 232 | fcvector_append_flag(FCVECTOR_HAS_NO_INLINE -fno-inline) 233 | fcvector_append_flag(FCVECTOR_HAS_STACK_PROTECTOR_ALL -fstack-protector-all) 234 | endif() 235 | 236 | # Optimization flags: release 237 | if (CMAKE_BUILD_TYPE STREQUAL "Release") 238 | fcvector_append_flag(FCVECTOR_HAS_OFAST -Ofast) 239 | fcvector_append_flag(FCVECTOR_HAS_UNDEBUG -UNDEBUG) 240 | fcvector_append_flag(FCVECTOR_HAS_MARCH_NATIVE "-march=native") 241 | fcvector_append_flag(FCVECTOR_HAS_MTUNE_NATIVE "-mtune=native") 242 | fcvector_append_flag(FCVECTOR_HAS_STRICT_ALIASING -fstrict-aliasing) 243 | fcvector_append_flag(FCVECTOR_HAS_VECTORIZE -fvectorize) 244 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly -mllvm -polly-vectorizer=stripmine") 245 | 246 | # If ASan not enabled: omit frame pointer 247 | if (NOT FCVECTOR_ENABLE_ASAN) 248 | fcvector_append_flag(FCVECTOR_HAS_OMIT_FRAME_POINTER -fomit-frame-pointer) 249 | endif() 250 | endif() 251 | 252 | if (NOT FCVECTOR_ENABLE_LIKELY) 253 | fcvector_append_flag(FCVECTOR_HAS_DISABLE_LIKELY -DFCVECTOR_DISABLE_LIKELY_MACROS) 254 | endif() 255 | 256 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// 3 | /// Test for inline_vector 4 | /// 5 | /// Most of the tests below are adapted from libc++: https://libcxx.llvm.org 6 | /// under the following license: 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // The LLVM Compiler Infrastructure 10 | // 11 | // This file is dual licensed under the MIT and the University of Illinois Open 12 | // Source Licenses. See LICENSE.TXT for details. 13 | // 14 | //===----------------------------------------------------------------------===// 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | //#include "utils.hpp" 21 | 22 | #define FCV_ASSERT(...) \ 23 | static_cast((__VA_ARGS__) \ 24 | ? void(0) \ 25 | : ::std::experimental::fcv_detail::assert_failure( \ 26 | static_cast(__FILE__), __LINE__, \ 27 | "assertion failed: " #__VA_ARGS__)) 28 | 29 | template struct std::experimental::fcv_detail::storage::zero_sized; 30 | template struct std::experimental::fcv_detail::storage::trivial; 31 | template struct std::experimental::fcv_detail::storage::non_trivial< 32 | std::unique_ptr, 10>; 33 | 34 | template struct std::experimental::fcv_detail::storage::zero_sized; 35 | template struct std::experimental::fcv_detail::storage::trivial; 36 | template struct std::experimental::fcv_detail::storage::non_trivial< 37 | const std::unique_ptr, 10>; 38 | 39 | // empty: 40 | template struct std::experimental::fixed_capacity_vector; 41 | 42 | // trivial non-empty: 43 | template struct std::experimental::fixed_capacity_vector; 44 | template struct std::experimental::fixed_capacity_vector; 45 | template struct std::experimental::fixed_capacity_vector; 46 | 47 | // non-trivial 48 | template struct std::experimental::fixed_capacity_vector; 49 | template struct std::experimental::fixed_capacity_vector; 50 | 51 | // move-only: 52 | template struct std::experimental::fixed_capacity_vector, 53 | 3>; 54 | template struct std::experimental::fixed_capacity_vector< 55 | const std::unique_ptr, 3>; 56 | 57 | struct [[gsl::suppress("cppcoreguidelines-special-member-functions")]] tint 58 | { 59 | std::size_t i; 60 | tint() = default; 61 | constexpr tint(tint const&) = default; 62 | constexpr tint(tint &&) = default; 63 | constexpr tint& operator=(tint const&) = default; 64 | constexpr tint& operator=(tint&&) = default; 65 | // FIXME: ~tint() = default; 66 | // ^^^ adding this makes the class non-trivial in clang 67 | 68 | explicit constexpr tint(std::size_t j) : i(j) 69 | { 70 | } 71 | explicit operator std::size_t() 72 | { 73 | return i; 74 | } 75 | }; 76 | 77 | static_assert(std::is_trivial{} and std::is_copy_constructible{} 78 | and std::is_move_constructible{}, 79 | ""); 80 | 81 | // Explicit instantiations 82 | template struct std::experimental::fixed_capacity_vector; // trivial empty 84 | template struct std::experimental::fixed_capacity_vector; // trivial 85 | // non-empty 86 | template struct std::experimental::fixed_capacity_vector; // trivial 87 | // nom-empty 88 | template struct std::experimental::fixed_capacity_vector; // trivial 89 | // nom-empty 90 | 91 | struct moint final 92 | { 93 | std::size_t i = 0; 94 | moint() = default; 95 | moint(moint const&) = delete; 96 | moint& operator=(moint const&) = delete; 97 | moint(moint&&) = default; 98 | moint& operator=(moint&&) = default; 99 | ~moint() = default; 100 | explicit operator std::size_t() 101 | { 102 | return i; 103 | } 104 | explicit constexpr moint(std::size_t j) : i(j) 105 | { 106 | } 107 | // it seems that deleting the copy constructor is not enough to make 108 | // this non-trivial using libstdc++: 109 | virtual void foo() 110 | { 111 | } 112 | bool operator==(moint b) 113 | { 114 | return i == b.i; 115 | } 116 | }; 117 | 118 | static_assert(!std::is_trivial{} and !std::is_copy_constructible{} 119 | and std::is_move_constructible{}, 120 | ""); 121 | 122 | // cannot explicitly instantiate the type for some types 123 | // // non-trivial empty: 124 | // template struct std::experimental::fixed_capacity_vector; 125 | // // non-trivial non-empty: 126 | // template struct std::experimental::fixed_capacity_vector; 127 | // template struct std::experimental::fixed_capacity_vector; 128 | // template struct std::experimental::fixed_capacity_vector; 129 | 130 | template 131 | using vector = std::experimental::fixed_capacity_vector; 132 | 133 | template 134 | constexpr bool test_bounds(vector const& v, std::size_t sz) 135 | { 136 | FCV_ASSERT(v.size() == sz); 137 | FCV_ASSERT(v.max_size() == N); 138 | FCV_ASSERT(v.capacity() == N); 139 | 140 | std::decay_t count = std::decay_t(); 141 | for (std::size_t i = 0; i != sz; ++i) 142 | { 143 | ++count; 144 | FCV_ASSERT(v[i] == count); 145 | } 146 | 147 | return true; 148 | } 149 | 150 | class non_copyable 151 | { 152 | int i_; 153 | double d_; 154 | 155 | public: 156 | non_copyable(const non_copyable&) = delete; 157 | non_copyable& operator=(const non_copyable&) = delete; 158 | 159 | non_copyable(int i, double d) : i_(i), d_(d) 160 | { 161 | } 162 | 163 | non_copyable(non_copyable&& a) noexcept : i_(a.i_), d_(a.d_) 164 | { 165 | a.i_ = 0; 166 | a.d_ = 0; 167 | } 168 | 169 | non_copyable& operator=(non_copyable&& a) noexcept 170 | { 171 | i_ = a.i_; 172 | d_ = a.d_; 173 | a.i_ = 0; 174 | a.d_ = 0; 175 | return *this; 176 | } 177 | 178 | int geti() const 179 | { 180 | return i_; 181 | } 182 | double getd() const 183 | { 184 | return d_; 185 | } 186 | }; 187 | 188 | template 189 | struct vec 190 | { 191 | vec() = default; 192 | vec(std::initializer_list /*il*/) 193 | { 194 | } 195 | }; 196 | 197 | int main() 198 | { 199 | { // storage 200 | using std::experimental::fcv_detail::storage::_t; 201 | using std::experimental::fcv_detail::storage::non_trivial; 202 | using std::experimental::fcv_detail::storage::trivial; 203 | using std::experimental::fcv_detail::storage::zero_sized; 204 | 205 | static_assert(std::is_same<_t, zero_sized>{}); 206 | static_assert(std::is_same<_t, trivial>{}); 207 | static_assert(std::is_same<_t, 10>, 208 | non_trivial, 10>>{}, 209 | ""); 210 | 211 | constexpr _t s({1, 2, 3, 4}); 212 | static_assert(s.size() == 4); 213 | 214 | constexpr _t s2({1, 2, 3, 4}); 215 | static_assert(s2.size() == 4); 216 | } 217 | 218 | { // const 219 | vector v0 = {}; 220 | test_bounds(v0, 0); 221 | 222 | constexpr vector vc0 = {}; 223 | test_bounds(vc0, 0); 224 | static_assert(test_bounds(vc0, 0), ""); 225 | 226 | // one and two elements initializer_list don't work 227 | vector v1 = {1}; 228 | test_bounds(v1, 1); 229 | // 230 | // constexpr vector vc1 = {1}; 231 | // test_bounds(vc1, 1); 232 | // static_assert(test_bounds(vc1, 1), ""); 233 | 234 | vector v3 = {1, 2, 3}; 235 | test_bounds(v3, 3); 236 | constexpr vector vc3 = {1, 2, 3}; 237 | test_bounds(vc3, 3); 238 | static_assert(test_bounds(vc3, 3), ""); 239 | } 240 | 241 | auto test_contiguous = [](auto&& c) { 242 | for (size_t i = 0; i < c.size(); ++i) 243 | { 244 | FCV_ASSERT(*(c.begin() + i) == *(std::addressof(*c.begin()) + i)); 245 | } 246 | }; 247 | 248 | { // contiguous 249 | using T = int; 250 | using C = vector; 251 | auto e = C(); 252 | FCV_ASSERT(e.empty()); 253 | test_contiguous(e); 254 | test_contiguous(C(3, 5)); 255 | } 256 | { // default construct element 257 | using T = int; 258 | using C = vector; 259 | C c(1); 260 | FCV_ASSERT(c.back() == 0); 261 | FCV_ASSERT(c.front() == 0); 262 | FCV_ASSERT(c[0] == 0); 263 | } 264 | 265 | { // iterator 266 | using T = int; 267 | using C = vector; 268 | C c; 269 | C::iterator i = c.begin(); 270 | C::iterator j = c.end(); 271 | FCV_ASSERT(std::distance(i, j) == 0); 272 | FCV_ASSERT(i == j); 273 | } 274 | { // const iterator 275 | using T = int; 276 | using C = vector; 277 | const C c{}; 278 | C::const_iterator i = c.begin(); 279 | C::const_iterator j = c.end(); 280 | FCV_ASSERT(std::distance(i, j) == 0); 281 | FCV_ASSERT(i == j); 282 | } 283 | { // cbegin/cend 284 | using T = int; 285 | using C = vector; 286 | C c; 287 | C::const_iterator i = c.cbegin(); 288 | C::const_iterator j = c.cend(); 289 | FCV_ASSERT(std::distance(i, j) == 0); 290 | FCV_ASSERT(i == j); 291 | FCV_ASSERT(i == c.end()); 292 | } 293 | { // iterator constructor 294 | using T = int; 295 | using C = vector; 296 | const T t[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 297 | C c(std::begin(t), std::end(t)); 298 | FCV_ASSERT( 299 | std::equal(std::begin(t), std::end(t), std::begin(c), std::end(c))); 300 | C::iterator i = c.begin(); 301 | FCV_ASSERT(*i == 0); 302 | ++i; 303 | FCV_ASSERT(*i == 1); 304 | *i = 10; 305 | FCV_ASSERT(*i == 10); 306 | FCV_ASSERT(std::distance(std::begin(c), std::end(c)) == 10); 307 | } 308 | { // N3644 testing 309 | using C = vector; 310 | C::iterator ii1{}, ii2{}; 311 | C::iterator ii4 = ii1; 312 | C::const_iterator cii{}; 313 | FCV_ASSERT(ii1 == ii2); 314 | FCV_ASSERT(ii1 == ii4); 315 | 316 | FCV_ASSERT(!(ii1 != ii2)); 317 | 318 | FCV_ASSERT((ii1 == cii)); 319 | FCV_ASSERT((cii == ii1)); 320 | FCV_ASSERT(!(ii1 != cii)); 321 | FCV_ASSERT(!(cii != ii1)); 322 | FCV_ASSERT(!(ii1 < cii)); 323 | FCV_ASSERT(!(cii < ii1)); 324 | FCV_ASSERT((ii1 <= cii)); 325 | FCV_ASSERT((cii <= ii1)); 326 | FCV_ASSERT(!(ii1 > cii)); 327 | FCV_ASSERT(!(cii > ii1)); 328 | FCV_ASSERT((ii1 >= cii)); 329 | FCV_ASSERT((cii >= ii1)); 330 | FCV_ASSERT((cii - ii1) == 0); 331 | FCV_ASSERT((ii1 - cii) == 0); 332 | } 333 | 334 | { // capacity 335 | vector a; 336 | static_assert(a.capacity() == std::size_t(10)); 337 | FCV_ASSERT(a.empty()); 338 | for (std::size_t i = 0; i != 10; ++i) 339 | { 340 | a.push_back(0); 341 | } 342 | static_assert(a.capacity() == std::size_t(10)); 343 | FCV_ASSERT(a.size() == std::size_t(10)); 344 | FCV_ASSERT(!a.empty()); 345 | } 346 | 347 | { // resize copyable 348 | using Copyable = int; 349 | vector a(std::size_t(10), 5); 350 | FCV_ASSERT(a.size() == std::size_t(10)); 351 | static_assert(a.capacity() == std::size_t(10)); 352 | test_contiguous(a); 353 | for (std::size_t i = 0; i != 10; ++i) 354 | { 355 | FCV_ASSERT(a[i] == 5); 356 | } 357 | a.resize(5); 358 | FCV_ASSERT(a.size() == std::size_t(5)); 359 | 360 | static_assert(a.capacity() == std::size_t(10)); 361 | test_contiguous(a); 362 | a.resize(9); 363 | FCV_ASSERT(a[4] == 5); 364 | for (std::size_t i = 5; i != 9; ++i) 365 | { 366 | FCV_ASSERT(a[i] == 0); 367 | } 368 | FCV_ASSERT(a.size() == std::size_t(9)); 369 | static_assert(a.capacity() == std::size_t(10)); 370 | test_contiguous(a); 371 | a.resize(10, 3); 372 | FCV_ASSERT(a[4] == 5); 373 | FCV_ASSERT(a[8] == 0); 374 | FCV_ASSERT(a[9] == 3); 375 | FCV_ASSERT(a.size() == std::size_t(10)); 376 | static_assert(a.capacity() == std::size_t(10)); 377 | a.resize(5, 2); 378 | for (std::size_t i = 0; i != 5; ++i) 379 | { 380 | FCV_ASSERT(a[i] == 5); 381 | } 382 | test_contiguous(a); 383 | } 384 | { // resize move-only 385 | using MoveOnly = std::unique_ptr; 386 | vector a(10); 387 | FCV_ASSERT(a.size() == std::size_t(10)); 388 | static_assert(a.capacity() == std::size_t(10)); 389 | a.resize(5); 390 | test_contiguous(a); 391 | FCV_ASSERT(a.size() == std::size_t(5)); 392 | static_assert(a.capacity() == std::size_t(10)); 393 | a.resize(9); 394 | FCV_ASSERT(a.size() == std::size_t(9)); 395 | static_assert(a.capacity() == std::size_t(10)); 396 | } 397 | 398 | { // resize value: 399 | using Copyable = int; 400 | vector a(std::size_t(10)); 401 | FCV_ASSERT(a.size() == std::size_t(10)); 402 | static_assert(a.capacity() == std::size_t(10)); 403 | test_contiguous(a); 404 | for (std::size_t i = 0; i != 10; ++i) 405 | { 406 | FCV_ASSERT(a[i] == 0); 407 | } 408 | a.resize(5); 409 | FCV_ASSERT(a.size() == std::size_t(5)); 410 | static_assert(a.capacity() == std::size_t(10)); 411 | test_contiguous(a); 412 | for (std::size_t i = 0; i != 5; ++i) 413 | { 414 | FCV_ASSERT(a[i] == 0); 415 | } 416 | a.resize(9, 5); 417 | for (std::size_t i = 0; i != 5; ++i) 418 | { 419 | FCV_ASSERT(a[i] == 0); 420 | } 421 | for (std::size_t i = 5; i != 9; ++i) 422 | { 423 | FCV_ASSERT(a[i] == 5); 424 | } 425 | FCV_ASSERT(a.size() == std::size_t(9)); 426 | static_assert(a.capacity() == std::size_t(10)); 427 | test_contiguous(a); 428 | a.resize(10, 3); 429 | for (std::size_t i = 0; i != 5; ++i) 430 | { 431 | FCV_ASSERT(a[i] == 0); 432 | } 433 | for (std::size_t i = 5; i != 9; ++i) 434 | { 435 | FCV_ASSERT(a[i] == 5); 436 | } 437 | FCV_ASSERT(a[9] == 3); 438 | FCV_ASSERT(a.size() == std::size_t(10)); 439 | static_assert(a.capacity() == std::size_t(10)); 440 | test_contiguous(a); 441 | } 442 | 443 | { // assign copy 444 | vector z(3, 5); 445 | vector a = {0, 1, 2}; 446 | FCV_ASSERT(a.size() == std::size_t{3}); 447 | vector b; 448 | FCV_ASSERT(b.size() == std::size_t{0}); 449 | b = a; 450 | FCV_ASSERT(b.size() == std::size_t{3}); 451 | FCV_ASSERT( 452 | std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b))); 453 | } 454 | 455 | { // copy construct 456 | vector a = {0, 1, 2}; 457 | FCV_ASSERT(a.size() == std::size_t{3}); 458 | vector b(a); 459 | FCV_ASSERT(b.size() == std::size_t{3}); 460 | 461 | FCV_ASSERT( 462 | std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b))); 463 | } 464 | 465 | { // assign move 466 | using MoveOnly = std::unique_ptr; 467 | vector a(3); 468 | FCV_ASSERT(a.size() == std::size_t{3}); 469 | vector b; 470 | FCV_ASSERT(b.size() == std::size_t{0}); 471 | b = std::move(a); 472 | FCV_ASSERT(b.size() == std::size_t{3}); 473 | [[gsl::suppress("misc-use-after-move")]] { 474 | FCV_ASSERT(a.size() == std::size_t{3}); 475 | } 476 | } 477 | 478 | { // move construct 479 | using MoveOnly = std::unique_ptr; 480 | vector a(3); 481 | FCV_ASSERT(a.size() == std::size_t{3}); 482 | vector b(std::move(a)); 483 | FCV_ASSERT(b.size() == std::size_t{3}); 484 | [[gsl::suppress("misc-use-after-move")]] { 485 | FCV_ASSERT(a.size() == std::size_t{3}); 486 | } 487 | } 488 | 489 | { // old tests 490 | using vec_t = vector; 491 | vec_t vec1(5); 492 | vec1[0] = 0; 493 | vec1[1] = 1; 494 | vec1[2] = 2; 495 | vec1[3] = 3; 496 | vec1[4] = 4; 497 | { 498 | vec_t vec2; 499 | vec2.push_back(5); 500 | vec2.push_back(6); 501 | vec2.push_back(7); 502 | vec2.push_back(8); 503 | vec2.push_back(9); 504 | FCV_ASSERT(vec1[0] == 0); 505 | FCV_ASSERT(vec1[4] == 4); 506 | FCV_ASSERT(vec2[0] == 5); 507 | FCV_ASSERT(vec2[4] == 9); 508 | } 509 | { 510 | auto vec2 = vec1; 511 | FCV_ASSERT(vec2[0] == 0); 512 | FCV_ASSERT(vec2[4] == 4); 513 | FCV_ASSERT(vec1[0] == 0); 514 | FCV_ASSERT(vec1[4] == 4); 515 | } 516 | { 517 | int count_ = 0; 518 | for (auto i : vec1) 519 | { 520 | FCV_ASSERT(i == count_); 521 | count_++; 522 | } 523 | } 524 | 525 | { 526 | std::vector vec2(5); 527 | vec2[0] = 4; 528 | vec2[1] = 3; 529 | vec2[2] = 2; 530 | vec2[3] = 1; 531 | vec2[4] = 0; 532 | vec_t vec(vec2.size()); 533 | copy(std::begin(vec2), std::end(vec2), std::begin(vec)); 534 | int count_ = 4; 535 | for (auto i : vec) 536 | { 537 | FCV_ASSERT(i == count_); 538 | count_--; 539 | } 540 | } 541 | } 542 | { 543 | using vec_t = vector; 544 | static_assert(sizeof(vec_t) == 1, ""); 545 | 546 | constexpr auto a = vec_t{}; 547 | static_assert(a.size() == std::size_t{0}, ""); 548 | } 549 | 550 | { // back and front: 551 | using C = vector; 552 | C c(1); 553 | FCV_ASSERT(c.back() == 0); 554 | FCV_ASSERT(c.front() == 0); 555 | FCV_ASSERT(c[0] == 0); 556 | c.clear(); 557 | int one = 1; 558 | c.push_back(one); 559 | FCV_ASSERT(c.back() == 1); 560 | FCV_ASSERT(c.front() == 1); 561 | FCV_ASSERT(c[0] == 1); 562 | FCV_ASSERT(c.size() == 1); 563 | c.push_back(2); 564 | FCV_ASSERT(c.back() == 2); 565 | FCV_ASSERT(c.front() == 1); 566 | FCV_ASSERT(c[0] == 1); 567 | FCV_ASSERT(c[1] == 2); 568 | FCV_ASSERT(c.size() == 2); 569 | c.pop_back(); 570 | FCV_ASSERT(c.front() == 1); 571 | FCV_ASSERT(c[0] == 1); 572 | FCV_ASSERT(c.back() == 1); 573 | c.pop_back(); 574 | FCV_ASSERT(c.empty()); 575 | } 576 | 577 | { // const back: 578 | using C = vector; 579 | constexpr C c(1); 580 | static_assert(c.back() == 0); 581 | static_assert(c.front() == 0); 582 | static_assert(c[0] == 0); 583 | static_assert(c.size() == 1); 584 | } 585 | 586 | { // swap: same type 587 | using C = vector; 588 | C c0(3, 5); 589 | C c1(5, 1); 590 | C c2(0); 591 | FCV_ASSERT(c0.size() == std::size_t(3)); 592 | FCV_ASSERT(c1.size() == std::size_t(5)); 593 | FCV_ASSERT(c2.size() == std::size_t(0)); 594 | for (std::size_t i = 0; i != 3; ++i) 595 | { 596 | FCV_ASSERT(c0[i] == 5); 597 | } 598 | for (std::size_t i = 0; i != 5; ++i) 599 | { 600 | FCV_ASSERT(c1[i] == 1); 601 | } 602 | c0.swap(c1); 603 | FCV_ASSERT(c0.size() == std::size_t(5)); 604 | FCV_ASSERT(c1.size() == std::size_t(3)); 605 | for (std::size_t i = 0; i != 5; ++i) 606 | { 607 | FCV_ASSERT(c0[i] == 1); 608 | } 609 | for (std::size_t i = 0; i != 3; ++i) 610 | { 611 | FCV_ASSERT(c1[i] == 5); 612 | } 613 | c2.swap(c1); 614 | FCV_ASSERT(c1.size() == std::size_t(0)); 615 | FCV_ASSERT(c2.size() == std::size_t(3)); 616 | for (std::size_t i = 0; i != 3; ++i) 617 | { 618 | FCV_ASSERT(c2[i] == 5); 619 | } 620 | } 621 | 622 | { // std::swap: same type 623 | using C = vector; 624 | C c0(3, 5); 625 | C c1(5, 1); 626 | C c2(0); 627 | FCV_ASSERT(c0.size() == std::size_t(3)); 628 | FCV_ASSERT(c1.size() == std::size_t(5)); 629 | FCV_ASSERT(c2.size() == std::size_t(0)); 630 | for (std::size_t i = 0; i != 3; ++i) 631 | { 632 | FCV_ASSERT(c0[i] == 5); 633 | } 634 | for (std::size_t i = 0; i != 5; ++i) 635 | { 636 | FCV_ASSERT(c1[i] == 1); 637 | } 638 | std::swap(c0, c1); 639 | FCV_ASSERT(c0.size() == std::size_t(5)); 640 | FCV_ASSERT(c1.size() == std::size_t(3)); 641 | for (std::size_t i = 0; i != 5; ++i) 642 | { 643 | FCV_ASSERT(c0[i] == 1); 644 | } 645 | for (std::size_t i = 0; i != 3; ++i) 646 | { 647 | FCV_ASSERT(c1[i] == 5); 648 | } 649 | std::swap(c2, c1); 650 | FCV_ASSERT(c1.size() == std::size_t(0)); 651 | FCV_ASSERT(c2.size() == std::size_t(3)); 652 | for (std::size_t i = 0; i != 3; ++i) 653 | { 654 | FCV_ASSERT(c2[i] == 5); 655 | } 656 | } 657 | 658 | { 659 | constexpr vector v; 660 | static_assert(v.data() != nullptr); 661 | 662 | constexpr vector v0; 663 | static_assert(v0.data() == nullptr); 664 | } 665 | 666 | {// emplace: 667 | {vector c; 668 | vector::iterator i = c.emplace(c.cbegin(), 2, 3.5); 669 | FCV_ASSERT(i == c.begin()); 670 | FCV_ASSERT(c.size() == 1); 671 | FCV_ASSERT(c.front().geti() == 2); 672 | FCV_ASSERT(c.front().getd() == 3.5); 673 | i = c.emplace(c.cend(), 3, 4.5); 674 | FCV_ASSERT(i == c.end() - 1); 675 | FCV_ASSERT(c.size() == 2); 676 | FCV_ASSERT(c.front().geti() == 2); 677 | FCV_ASSERT(c.front().getd() == 3.5); 678 | FCV_ASSERT(c.back().geti() == 3); 679 | FCV_ASSERT(c.back().getd() == 4.5); 680 | i = c.emplace(c.cbegin() + 1, 4, 6.5); 681 | FCV_ASSERT(i == c.begin() + 1); 682 | FCV_ASSERT(c.size() == 3); 683 | FCV_ASSERT(c.front().geti() == 2); 684 | FCV_ASSERT(c.front().getd() == 3.5); 685 | FCV_ASSERT(c[1].geti() == 4); 686 | FCV_ASSERT(c[1].getd() == 6.5); 687 | FCV_ASSERT(c.back().geti() == 3); 688 | FCV_ASSERT(c.back().getd() == 4.5); 689 | } 690 | { 691 | vector c; 692 | vector::iterator i = c.emplace(c.cbegin(), 2, 3.5); 693 | FCV_ASSERT(i == c.begin()); 694 | FCV_ASSERT(c.size() == 1); 695 | FCV_ASSERT(c.front().geti() == 2); 696 | FCV_ASSERT(c.front().getd() == 3.5); 697 | i = c.emplace(c.cend(), 3, 4.5); 698 | FCV_ASSERT(i == c.end() - 1); 699 | FCV_ASSERT(c.size() == 2); 700 | FCV_ASSERT(c.front().geti() == 2); 701 | FCV_ASSERT(c.front().getd() == 3.5); 702 | FCV_ASSERT(c.back().geti() == 3); 703 | FCV_ASSERT(c.back().getd() == 4.5); 704 | i = c.emplace(c.cbegin() + 1, 4, 6.5); 705 | FCV_ASSERT(i == c.begin() + 1); 706 | FCV_ASSERT(c.size() == 3); 707 | FCV_ASSERT(c.front().geti() == 2); 708 | FCV_ASSERT(c.front().getd() == 3.5); 709 | FCV_ASSERT(c[1].geti() == 4); 710 | FCV_ASSERT(c[1].getd() == 6.5); 711 | FCV_ASSERT(c.back().geti() == 3); 712 | FCV_ASSERT(c.back().getd() == 4.5); 713 | } 714 | } 715 | 716 | {// emplace_back 717 | {vector c; 718 | c.emplace_back(2, 3.5); 719 | FCV_ASSERT(c.size() == 1); 720 | FCV_ASSERT(c.front().geti() == 2); 721 | FCV_ASSERT(c.front().getd() == 3.5); 722 | c.emplace_back(3, 4.5); 723 | FCV_ASSERT(c.size() == 2); 724 | FCV_ASSERT(c.front().geti() == 2); 725 | FCV_ASSERT(c.front().getd() == 3.5); 726 | FCV_ASSERT(c.back().geti() == 3); 727 | FCV_ASSERT(c.back().getd() == 4.5); 728 | } 729 | { 730 | vector c; 731 | c.emplace_back(2, 3.5); 732 | FCV_ASSERT(c.size() == 1); 733 | FCV_ASSERT(c.front().geti() == 2); 734 | FCV_ASSERT(c.front().getd() == 3.5); 735 | c.emplace_back(3, 4.5); 736 | FCV_ASSERT(c.size() == 2); 737 | FCV_ASSERT(c.front().geti() == 2); 738 | FCV_ASSERT(c.front().getd() == 3.5); 739 | FCV_ASSERT(c.back().geti() == 3); 740 | FCV_ASSERT(c.back().getd() == 4.5); 741 | } 742 | } 743 | 744 | { // emplace extra: 745 | {// 746 | vector v; 747 | v = {1, 2, 3}; 748 | 749 | v.emplace(v.begin(), v.back()); 750 | FCV_ASSERT(v[0] == 3); 751 | } 752 | { 753 | vector v; 754 | v = {1, 2, 3}; 755 | v.emplace(v.begin(), v.back()); 756 | FCV_ASSERT(v[0] == 3); 757 | } 758 | } 759 | 760 | {// erase 761 | {int a1[] = {1, 2, 3}; 762 | vector l1(a1, a1 + 3); 763 | FCV_ASSERT(l1.size() == 3); 764 | vector::const_iterator i = l1.begin(); 765 | ++i; 766 | vector::iterator j = l1.erase(i); 767 | FCV_ASSERT(l1.size() == 2); 768 | FCV_ASSERT(std::distance(l1.begin(), l1.end()) == 2); 769 | FCV_ASSERT(*j == 3); 770 | FCV_ASSERT(*l1.begin() == 1); 771 | FCV_ASSERT(*std::next(l1.begin()) == 3); 772 | j = l1.erase(j); 773 | FCV_ASSERT(j == l1.end()); 774 | FCV_ASSERT(l1.size() == 1); 775 | FCV_ASSERT(std::distance(l1.begin(), l1.end()) == 1); 776 | FCV_ASSERT(*l1.begin() == 1); 777 | j = l1.erase(l1.begin()); 778 | FCV_ASSERT(j == l1.end()); 779 | FCV_ASSERT(l1.empty()); 780 | FCV_ASSERT(std::distance(l1.begin(), l1.end()) == 0); 781 | } 782 | } 783 | 784 | { // erase iter iter 785 | int a1[] = {1, 2, 3}; 786 | using vec_t = vector; 787 | { 788 | vec_t l1(a1, a1 + 3); 789 | vec_t::iterator i = l1.erase(l1.cbegin(), l1.cbegin()); 790 | FCV_ASSERT(l1.size() == 3); 791 | FCV_ASSERT(std::distance(l1.cbegin(), l1.cend()) == 3); 792 | FCV_ASSERT(i == l1.begin()); 793 | } 794 | { 795 | vec_t l1(a1, a1 + 3); 796 | vec_t::iterator i = l1.erase(l1.cbegin(), std::next(l1.cbegin())); 797 | FCV_ASSERT(l1.size() == 2); 798 | FCV_ASSERT(std::distance(l1.cbegin(), l1.cend()) == 2); 799 | FCV_ASSERT(i == l1.begin()); 800 | FCV_ASSERT(l1 == vec_t(a1 + 1, a1 + 3)); 801 | } 802 | { 803 | vec_t l1(a1, a1 + 3); 804 | vec_t::iterator i = l1.erase(l1.cbegin(), std::next(l1.cbegin(), 2)); 805 | FCV_ASSERT(l1.size() == 1); 806 | FCV_ASSERT(std::distance(l1.cbegin(), l1.cend()) == 1); 807 | FCV_ASSERT(i == l1.begin()); 808 | FCV_ASSERT(l1 == vec_t(a1 + 2, a1 + 3)); 809 | } 810 | { 811 | vec_t l1(a1, a1 + 3); 812 | vec_t::iterator i = l1.erase(l1.cbegin(), std::next(l1.cbegin(), 3)); 813 | FCV_ASSERT(l1.empty()); 814 | FCV_ASSERT(std::distance(l1.cbegin(), l1.cend()) == 0); 815 | FCV_ASSERT(i == l1.begin()); 816 | } 817 | { 818 | vector outer(2, vec_t(1)); 819 | outer.erase(outer.begin(), outer.begin()); 820 | FCV_ASSERT(outer.size() == 2); 821 | FCV_ASSERT(outer[0].size() == 1); 822 | FCV_ASSERT(outer[1].size() == 1); 823 | } 824 | } 825 | 826 | {// insert init list 827 | {vector d(10, 1); 828 | vector::iterator i = d.insert(d.cbegin() + 2, {3, 4, 5, 6}); 829 | FCV_ASSERT(d.size() == 14); 830 | FCV_ASSERT(i == d.begin() + 2); 831 | FCV_ASSERT(d[0] == 1); 832 | FCV_ASSERT(d[1] == 1); 833 | FCV_ASSERT(d[2] == 3); 834 | FCV_ASSERT(d[3] == 4); 835 | FCV_ASSERT(d[4] == 5); 836 | FCV_ASSERT(d[5] == 6); 837 | FCV_ASSERT(d[6] == 1); 838 | FCV_ASSERT(d[7] == 1); 839 | FCV_ASSERT(d[8] == 1); 840 | FCV_ASSERT(d[9] == 1); 841 | FCV_ASSERT(d[10] == 1); 842 | FCV_ASSERT(d[11] == 1); 843 | FCV_ASSERT(d[12] == 1); 844 | FCV_ASSERT(d[13] == 1); 845 | } 846 | } 847 | 848 | {// insert iter iter 849 | {vector v(100); 850 | int a[] = {1, 2, 3, 4, 5}; 851 | const std::size_t n = sizeof(a) / sizeof(a[0]); 852 | vector::iterator i = v.insert(v.cbegin() + 10, (a + 0), (a + n)); 853 | FCV_ASSERT(v.size() == 100 + n); 854 | FCV_ASSERT(i == v.begin() + 10); 855 | std::size_t j; 856 | for (j = 0; j < 10; ++j) 857 | { 858 | FCV_ASSERT(v[j] == 0); 859 | } 860 | for (std::size_t k = 0; k < n; ++j, ++k) 861 | { 862 | FCV_ASSERT(v[j] == a[k]); 863 | } 864 | for (; j < 105; ++j) 865 | { 866 | FCV_ASSERT(v[j] == 0); 867 | } 868 | } 869 | [[gsl::suppress("cppcoreguidelines-pro-bounds-pointer-arithmetic")]] { 870 | vector v(100); 871 | size_t sz = v.size(); 872 | int a[] = {1, 2, 3, 4, 5}; 873 | const unsigned n = sizeof(a) / sizeof(a[0]); 874 | vector::iterator i = v.insert(v.cbegin() + 10, (a + 0), (a + n)); 875 | FCV_ASSERT(v.size() == sz + n); 876 | FCV_ASSERT(i == v.begin() + 10); 877 | std::size_t j; 878 | for (j = 0; j < 10; ++j) 879 | { 880 | FCV_ASSERT(v[j] == 0); 881 | } 882 | for (std::size_t k = 0; k < n; ++j, ++k) 883 | { 884 | FCV_ASSERT(v[j] == a[k]); 885 | } 886 | for (; j < v.size(); ++j) 887 | { 888 | FCV_ASSERT(v[j] == 0); 889 | } 890 | } 891 | } 892 | 893 | {// insert iter rvalue 894 | {vector v(100); 895 | vector::iterator i = v.insert(v.cbegin() + 10, moint(3)); 896 | FCV_ASSERT(v.size() == 101); 897 | FCV_ASSERT(i == v.begin() + 10); 898 | std::size_t j; 899 | for (j = 0; j < 10; ++j) 900 | { 901 | FCV_ASSERT(v[j] == moint()); 902 | } 903 | FCV_ASSERT(v[j] == moint(3)); 904 | for (++j; j < 101; ++j) 905 | { 906 | FCV_ASSERT(v[j] == moint()); 907 | } 908 | } 909 | } 910 | 911 | {// insert iter size 912 | {vector v(100); 913 | vector::iterator i = v.insert(v.cbegin() + 10, 5, 1); 914 | FCV_ASSERT(v.size() == 105); 915 | FCV_ASSERT(i == v.begin() + 10); 916 | std::size_t j; 917 | for (j = 0; j < 10; ++j) 918 | { 919 | FCV_ASSERT(v[j] == 0); 920 | } 921 | for (; j < 15; ++j) 922 | { 923 | FCV_ASSERT(v[j] == 1); 924 | } 925 | for (++j; j < 105; ++j) 926 | { 927 | FCV_ASSERT(v[j] == 0); 928 | } 929 | } 930 | { 931 | vector v(100); 932 | size_t sz = v.size(); 933 | vector::iterator i = v.insert(v.cbegin() + 10, 5, 1); 934 | FCV_ASSERT(v.size() == sz + 5); 935 | FCV_ASSERT(i == v.begin() + 10); 936 | std::size_t j; 937 | for (j = 0; j < 10; ++j) 938 | { 939 | FCV_ASSERT(v[j] == 0); 940 | } 941 | for (; j < 15; ++j) 942 | { 943 | FCV_ASSERT(v[j] == 1); 944 | } 945 | for (++j; j < v.size(); ++j) 946 | { 947 | FCV_ASSERT(v[j] == 0); 948 | } 949 | } 950 | { 951 | vector v(100); 952 | size_t sz = v.size(); 953 | vector::iterator i = v.insert(v.cbegin() + 10, 5, 1); 954 | FCV_ASSERT(v.size() == sz + 5); 955 | FCV_ASSERT(i == v.begin() + 10); 956 | std::size_t j; 957 | for (j = 0; j < 10; ++j) 958 | { 959 | FCV_ASSERT(v[j] == 0); 960 | } 961 | for (; j < 15; ++j) 962 | { 963 | FCV_ASSERT(v[j] == 1); 964 | } 965 | for (++j; j < v.size(); ++j) 966 | { 967 | FCV_ASSERT(v[j] == 0); 968 | } 969 | } 970 | } 971 | 972 | {// iter value: 973 | {vector v(100); 974 | vector::iterator i = v.insert(v.cbegin() + 10, 1); 975 | FCV_ASSERT(v.size() == 101); 976 | FCV_ASSERT(i == v.begin() + 10); 977 | std::size_t j; 978 | for (j = 0; j < 10; ++j) 979 | { 980 | FCV_ASSERT(v[j] == 0); 981 | } 982 | FCV_ASSERT(v[j] == 1); 983 | for (++j; j < 101; ++j) 984 | { 985 | FCV_ASSERT(v[j] == 0); 986 | } 987 | } 988 | { 989 | vector v(100); 990 | size_t sz = v.size(); 991 | vector::iterator i = v.insert(v.cbegin() + 10, 1); 992 | FCV_ASSERT(v.size() == sz + 1); 993 | FCV_ASSERT(i == v.begin() + 10); 994 | std::size_t j; 995 | for (j = 0; j < 10; ++j) 996 | { 997 | FCV_ASSERT(v[j] == 0); 998 | } 999 | FCV_ASSERT(v[j] == 1); 1000 | for (++j; j < v.size(); ++j) 1001 | { 1002 | FCV_ASSERT(v[j] == 0); 1003 | } 1004 | } 1005 | { 1006 | vector v(100); 1007 | v.pop_back(); 1008 | v.pop_back(); // force no reallocation 1009 | size_t sz = v.size(); 1010 | vector::iterator i = v.insert(v.cbegin() + 10, 1); 1011 | FCV_ASSERT(v.size() == sz + 1); 1012 | FCV_ASSERT(i == v.begin() + 10); 1013 | std::size_t j; 1014 | for (j = 0; j < 10; ++j) 1015 | { 1016 | FCV_ASSERT(v[j] == 0); 1017 | } 1018 | FCV_ASSERT(v[j] == 1); 1019 | for (++j; j < v.size(); ++j) 1020 | { 1021 | FCV_ASSERT(v[j] == 0); 1022 | } 1023 | } 1024 | } 1025 | 1026 | { // push back move only 1027 | { 1028 | vector c; 1029 | c.push_back(moint(0)); 1030 | FCV_ASSERT(c.size() == 1); 1031 | for (std::size_t j = 0; j < c.size(); ++j) 1032 | { 1033 | FCV_ASSERT(c[j] == moint(j)); 1034 | } 1035 | c.push_back(moint(1)); 1036 | FCV_ASSERT(c.size() == 2); 1037 | for (std::size_t j = 0; j < c.size(); ++j) 1038 | { 1039 | FCV_ASSERT(c[j] == moint(j)); 1040 | } 1041 | c.push_back(moint(2)); 1042 | FCV_ASSERT(c.size() == 3); 1043 | for (std::size_t j = 0; j < c.size(); ++j) 1044 | { 1045 | FCV_ASSERT(c[j] == moint(j)); 1046 | } 1047 | c.push_back(moint(3)); 1048 | FCV_ASSERT(c.size() == 4); 1049 | for (std::size_t j = 0; j < c.size(); ++j) 1050 | { 1051 | FCV_ASSERT(c[j] == moint(j)); 1052 | } 1053 | c.push_back(moint(4)); 1054 | FCV_ASSERT(c.size() == 5); 1055 | for (std::size_t j = 0; j < c.size(); ++j) 1056 | { 1057 | FCV_ASSERT(c[j] == moint(j)); 1058 | } 1059 | } 1060 | } 1061 | 1062 | return 0; 1063 | } 1064 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # static_vector 2 | 3 | > A dynamically-resizable vector with fixed capacity and embedded storage (revision 3) 4 | 5 | **Document number**: P0843r3. 6 | 7 | **Date**: 2019-01-20. 8 | 9 | **Project**: Programming Language C++, Library Working Group. 10 | 11 | **Audience**: LEWG. 12 | 13 | **Reply-to**: Gonzalo Brito Gadeschi . 14 | 15 | # Table of contents 16 | 17 | - [1. Introduction](#INTRODUCTION) 18 | - [2. Motivation](#MOTIVATION) 19 | - [3. Existing practice](#EXISTING_PRACTICE) 20 | - [4. Design Decisions](#DESIGN) 21 | - [4.1 Storage/Memory Layout](#STORAGE) 22 | - [4.2 Move semantics](#MOVE) 23 | - [4.3 `constexpr` support](#CONSTEXPR) 24 | - [4.4 Exception safety](#EXCEPTION) 25 | - [4.5 Iterator invalidation](#ITERATOR) 26 | - [4.6 Naming](#NAMING) 27 | - [4.7 Potential extensions](#EXTENSIONS) 28 | - [5. Technical Specification](#TECHNICAL_SPECIFICATION) 29 | - [5.1 Overview](#OVERVIEW) 30 | - [5.2 Construction](#CONSTRUCTION) 31 | - [5.3 Destruction](#DESTRUCTION) 32 | - [5.4 Size and capacity](#SIZE) 33 | - [5.5 Element and data access](#ACCESS) 34 | - [5.6 Modifiers](#MODIFIERS) 35 | - [5.7 Specialized algorithms](#SPEC_ALG) 36 | - [6. Acknowledgments](#ACKNOWLEDGEMENTS) 37 | - [7. References](#REFERENCES) 38 | 39 | ### Changelog 40 | 41 | #### Revision 4 42 | 43 | - LEWG suggested that `push_back` should be UB when the capacity is exceeded 44 | - LEWG suggested that this should be a free-standing header 45 | 46 | #### Revision 3 47 | 48 | - Include LWG design questions for LEWG. 49 | - Incorporates LWG feedback. 50 | 51 | #### Revision 2 52 | 53 | - Replace the placeholder name `fixed_capacity_vector` with `static_vector` 54 | - Remove `at` checked element access member function. 55 | - Add changelog section. 56 | 57 | #### Revision 1 58 | 59 | - Minor style changes and bugfixes. 60 | 61 | # Design Questions from LWG to LEWG 62 | 63 | LWG asks LEWG to re-consider the following two design decisions: 64 | 65 | * In this document, exceeding the capacity in methods like `static_vector::push_back` is a pre-condition violation, that is, if the capacity is exceeded, the behavior is undefined. LWG suggested that exceeding the capacity in these methods should `std::abort` instead. The trade-offs in this space are discussed in Section [4.4 Exception safety](#EXCEPTION) of this proposal. 66 | 67 | * In this document, `` is a _free-standing_ header, this is now clarified in Section [5. Technical Specification](#TECHNICAL_SPECIFICATION). LWG suggests that `static_vector` should be included in `` instead. 68 | 69 | # 1. Introduction 70 | 71 | This paper proposes a modernized version of 72 | [`boost::container::static_vector`][boost_static_vector] [1]. That 73 | is, a dynamically-resizable `vector` with compile-time fixed capacity and 74 | contiguous embedded storage in which the elements are stored within the vector 75 | object itself. 76 | 77 | Its API closely resembles that of `std::vector`. It is a contiguous 78 | container with `O(1)` insertion and removal of elements at the end 79 | (non-amortized) and worst case `O(size())` insertion and removal otherwise. Like 80 | `std::vector`, the elements are initialized on insertion and destroyed on 81 | removal. For trivial `value_type`s, the vector is fully usable inside 82 | `constexpr` functions. 83 | 84 | # 2. Motivation and Scope 85 | 86 | The `static_vector` container is useful when: 87 | 88 | - memory allocation is not possible, e.g., embedded environments without a free 89 | store, where only a stack and the static memory segment are available, 90 | - memory allocation imposes an unacceptable performance penalty, e.g., with 91 | respect to latency, 92 | - allocation of objects with complex lifetimes in the _static_-memory segment is 93 | required, 94 | - `std::array` is not an option, e.g., if non-default constructible objects must 95 | be stored, 96 | - a dynamically-resizable array is required within `constexpr` functions, 97 | - the storage location of the `static_vector` elements is required to be within 98 | the `static_vector` object itself (e.g. to support `memcopy` for serialization 99 | purposes). 100 | 101 | # 3. Existing practice 102 | 103 | There are at least 3 widely used implementations of `static_vector`: 104 | [Boost.Container][boost_static_vector] [1], [EASTL][eastl] [2], and 105 | [Folly][folly] [3]. The main difference between these is that `Boost.Container` 106 | implements `static_vector` as a standalone type with its own guarantees, while 107 | both EASTL and Folly implement it by adding an extra template parameter to their 108 | `small_vector` types. 109 | 110 | A `static_vector` can also be poorly emulated by using a custom 111 | allocator, like for example [Howard Hinnant's `stack_alloc`][stack_alloc] [4], 112 | on top of `std::vector`. 113 | 114 | This proposal shares a similar purpose with [P0494R0][contiguous_container] [5] 115 | and [P0597R0: `std::constexpr_vector`][constexpr_vector_1] [6]. The main 116 | difference is that this proposal closely follows 117 | [`boost::container::static_vector`][boost_static_vector] [1] and proposes to 118 | standardize existing practice. A prototype implementation of this proposal for 119 | standardization purposes is provided here: 120 | [`http://github.com/gnzlbg/fixed_capacity_vector`][fixed_capacity_vector]. 121 | 122 | # 4. Design Decisions 123 | 124 | The most fundamental question that must be answered is: 125 | 126 | > Should `static_vector` be a standalone type or a special case of some other 127 | > type? 128 | 129 | The [EASTL][eastl] [2] and [Folly][folly] [3] special case `small_vector`, e.g., 130 | using a 4th template parameter, to make it become a `static_vector`. The paper 131 | [P0639R0: Changing attack vector of the `constexpr_vector`][constexpr_vector_2] 132 | [7] proposes improving the `Allocator` concepts to allow `static_vector`, among 133 | others, to be implemented as a special case of `std::vector` with a custom 134 | allocator. 135 | 136 | Both approaches run into the same fundamental issue: `static_vector` methods are 137 | identically-named to those of `std::vector` yet they have subtly different 138 | effects, exception-safety, iterator invalidation, and complexity guarantees. 139 | 140 | This proposal 141 | follows 142 | [`boost::container::static_vector`][boost_static_vector] [1] 143 | closely and specifies the semantics that `static_vector` ought to have 144 | as a standalone type. As a library component this delivers immediate 145 | value. 146 | 147 | I hope that having the concise semantics of this type specified will also be 148 | helpful for those that want to generalize the `Allocator` interface to allow 149 | implementing `static_vector` as a `std::vector` with a custom allocator. 150 | 151 | ## 4.1 Storage/Memory Layout 152 | 153 | The container models `ContiguousContainer`. The elements of the `static_vector` 154 | are contiguously stored and properly aligned within the `static_vector` object 155 | itself. The exact location of the contiguous elements within the `static_vector` 156 | is not specified. If the `Capacity` is zero the container has zero size: 157 | 158 | ```c++ 159 | static_assert(is_empty_v>); // for all T 160 | ``` 161 | 162 | This optimization is easily implementable, enables the EBO, and felt right. 163 | 164 | ## 4.2 Move semantics 165 | 166 | The move semantics of `static_vector` are equal to those of 167 | `std::array`. That is, after 168 | 169 | ```c++ 170 | static_vector a(10); 171 | static_vector b(std::move(a)); 172 | ``` 173 | 174 | the elements of `a` have been moved element-wise into `b`, the elements of `a` 175 | are left in an initialized but unspecified state (have been moved from state), 176 | the size of `a` is not altered, and `a.size() == b.size()`. 177 | 178 | Note: this behavior differs from `std::vector`, in particular for 179 | the similar case in which 180 | `std::allocator_traits::propagate_on_container_move_assignment` is 181 | `false`. In this situation the state of `std::vector` is initialized but 182 | unspecified. 183 | 184 | ## 4.3 `constexpr` support 185 | 186 | The API of `static_vector` is `constexpr`. If 187 | `is_trivially_copyable_v && is_default_constructible_v` is `true`, 188 | `static_vector`s can be seamlessly used 189 | from `constexpr` code. This allows using `static_vector` as a 190 | `constexpr_vector` to, e.g., implement other constexpr containers. 191 | 192 | The implementation cost of this is small: the prototye implementation 193 | specializes the storage for trivial types to use a C array with 194 | value-initialized elements and a defaulted destructor. 195 | 196 | This changes the algorithmic complexity of `static_vector` 197 | constructors for trivial-types from "Linear in `N`" to "Constant 198 | in `Capacity`. When the value-initialization takes place at run-time, 199 | this difference in behavior might be signficiant: 200 | `static_vector(4)` will only 201 | initialize 4 elements but 202 | `static_vector(4)` 203 | must value-initialize the `38721943228473 - 4` excess elements to be a 204 | valid `constexpr` constructor. 205 | 206 | Very large `static_vector`'s are not the 207 | target use case of this container class and will have, in general, worse 208 | performance than, e.g., `std::vector` (e.g. due to moves being `O(N)`). 209 | 210 | Future improvements to `constexpr` (e.g. being able to properly use 211 | `std::aligned_storage` in constexpr contexts) allow improving 212 | the performance of `static_vector` in a backwards 213 | compatible way. 214 | 215 | ## 4.4 Exception Safety 216 | 217 | The only operations that can actually fail within `static_vector` are: 219 | 220 | 1. `value_type`'s constructors/assignment/destructors/swap can potentially 221 | throw, 222 | 223 | 2. Mutating operations exceeding the capacity (`push_back`, `insert`, 224 | `emplace`, `static_vector(value_type, size)`, 225 | `static_vector(begin, end)`...). 226 | 227 | 3. Out-of-bounds unchecked access: 228 | - 3.1 `front`/`back`/`pop_back` when empty, `operator[]` (unchecked 229 | random-access). 230 | 231 | When `value_type`'s operations are invoked, the exception safety guarantees of 232 | `static_vector` depend on whether these operations can throw. This is 233 | detected with `noexcept`. 234 | 235 | Since its `Capacity` is fixed at compile-time, `static_vector` never 236 | dynamically allocates memory, the answer to the following question determines 237 | the exception safety for all other operations: 238 | 239 | > What should `static_vector` do when its `Capacity` is exceeded? 240 | 241 | Three main answers were explored in the prototype implementation: 242 | 243 | 1. Throw an exception. 244 | 2. Abort the process. 245 | 3. Make this a precondition violation. 246 | 247 | 248 | Throwing an exception is appealing because it makes the interface slightly more 249 | similar to that of `std::vector`. However, which exception should be thrown? It 250 | cannot be `std::bad_alloc`, because nothing is being allocated. It could throw 251 | either `std::out_of_bounds` or `std::logic_error` but in any case the interface 252 | does not end up being equal to that of `std::vector`. 253 | 254 | Aborting the process avoids the perils of undefined behavior but comes at the 255 | cost of enforcing a particular "error handling" mechanism in the implementation, 256 | which would not allow extending it to use, e.g., Contracts, in the future. 257 | 258 | The alternative is to make not exceeding the capacity a precondition on the 259 | `static_vector`'s methods. This approach allows implementations to provide good 260 | run-time diagnostics if they so desire, e.g., on debug builds by means of an 261 | assertion, and makes implementation that avoid run-time checks conforming as 262 | well. Since the mutating methods have a precondition, they have narrow 263 | contracts, and are not conditionally `noexcept`. This provides implementations 264 | that desire throwing an exception the freedom to do so, and it also provides the 265 | standard the freedom to improve these APIs by using contracts in the future. 266 | 267 | This proposal previously chooses this path and makes exceeding the 268 | `static_vector`'s capacity a precondition violation that results in undefined 269 | behavior. Throwing `checked_xxx` methods can be provided in a backwards 270 | compatible way. 271 | 272 | ## 4.5 Iterator invalidation 273 | 274 | The iterator invalidation rules are different than those for `std::vector`, 275 | since: 276 | 277 | - moving a `static_vector` invalidates all iterators, 278 | - swapping two `static_vector`s invalidates all iterators, and 279 | - inserting elements at the end of an `static_vector` never invalidates 280 | iterators. 281 | 282 | The following functions can potentially invalidate the iterators of 283 | `static_vector`s: `resize(n)`, `resize(n, v)`, `pop_back`, `erase`, and `swap`. 284 | 285 | ## 4.6 Naming 286 | 287 | The `static_vector` name was chosen after considering the following names via a 288 | poll in LEWG: 289 | 290 | - `array_vector`: a vector whose storage is backed up by a raw array. 291 | - `bounded_vector`: clearly indicates that the the size of the vector is bounded. 292 | - `fixed_capacity_vector`: clearly indicates that the capacity is fixed. 293 | - `static_capacity_vector`: clearly indicates that the capacity is fixed at compile time (static is overloaded). 294 | - `static_vector` (Boost.Container): due to "static" / compile-time allocation 295 | of the elements. The term `static` is, however, overloaded in C++ (e.g. 296 | `static` memory?). 297 | - `embedded_vector`: since the elements are "embedded" within the 298 | `fixed_capacity_vector` object itself. Sadly, the name `embedded` is 299 | overloaded, e.g., embedded systems. 300 | - `inline_vector`: the elements are stored "inline" within the 301 | `fixed_capacity_vector` object itself. The term `inline` is, however, already 302 | overloaded in C++ (e.g. `inline` functions => ODR, inlining, `inline` 303 | variables). 304 | - `stack_vector`: to denote that the elements can be stored on the stack. Is 305 | confusing since the elements can be on the stack, the heap, or the static 306 | memory segment. It also has a resemblance with `std::stack`. 307 | - `limited_vector` 308 | - `vector_n` 309 | 310 | The names `static_vector` and `vector_n` tied in the number of votes. Many users 311 | are already familiar with the most widely implementation of this container 312 | (`boost::container::static_vector`), which gives `static_vector` an edge over a 313 | completely new name. 314 | 315 | ## 4.7 Future extensions 316 | 317 | The following extensions could be added in a backwards compatible way: 318 | 319 | - utilities for hiding the concrete type of vector-like containers (e.g. 320 | `any_vector_ref`/`any_vector`). 321 | 322 | - default-initialization of the vector elements (as opposed to 323 | value-initialization): e.g. by using a tagged constructor with a 324 | `default_initialized_t` tag. 325 | 326 | - tagged-constructor of the form `static_vector(with_size_t, std::size_t 327 | N, T const& t = T())` to avoid the complexity introduced by initializer lists 328 | and braced initialization: 329 | 330 | ```c++ 331 | using vec_t = static_vector; 332 | vec_t v0(2); // two-elements: 0, 0 333 | vec_t v1{2}; // one-element: 2 334 | vec_t v2(2, 1); // two-elements: 1, 1 335 | vec_t v3{2, 1}; // two-elements: 2, 1 336 | ``` 337 | 338 | All these extensions are generally useful and not part of this proposal. 339 | 340 | # 5. Technical specification 341 | 342 | --- 343 | 344 | Note to editor: This enhancement is a pure header-only addition to the C++ 345 | standard library as the _freestanding_ `` header. It belongs in 346 | the "Sequence containers" (`\ref{sequences}`) part of the "Containers library" 347 | (`\ref{containers}`) as "Class template `static_vector`". 348 | 349 | Note to LWG: one of the primary use cases for this container is 350 | embedded/freestanding. An alternative to adding a new `` header 351 | would be to add `static_vector` to any of the _freestanding_ headers. None of 352 | the current _freestanding_ headers is a good semantic fit. 353 | 354 | --- 355 | 356 | ## 5. Class template `static_vector` 357 | 358 | Changes to `library.requirements.organization.headers` table "C++ library 359 | headers", add a new header: ``. 360 | 361 | Changes to `library.requirements.organization.compliance` table "C++ headers for 362 | freestanding implementations", add a new row: 363 | 364 | > [static_vector] Static vector `` 365 | 366 | Changes to `container.requirements.general`. 367 | 368 | The note of Table "Container Requirements" should be changed to contain 369 | `static_vector` as well: 370 | 371 | ```diff 372 | Those entries marked “(Note A)” or “(Note B)” have linear complexity 373 | - for array 374 | + for array and static_vector 375 | and have constant complexity for all other standard containers. 376 | [ Note: The algorithm equal() is defined in [algorithms]. — end note ] 377 | ``` 378 | 379 | Changes to `sequence.reqmts.1`: 380 | 381 | ```diff 382 | The library provides four basic kinds of sequence containers: vector, 383 | - forward_­list, list, and deque. 384 | + static_vector, forward_­list, list, and deque. 385 | ``` 386 | 387 | Changes to `sequence.reqmts.2`: 388 | 389 | ```diff 390 | vector is the type of sequence container that should be used by default. 391 | + static_vector should be used when the container has a fixed capacity known during translation. 392 | array should be used when the container has a fixed size known during translation. 393 | ``` 394 | 395 | ### 5.1 Class template `static_vector` overview 396 | 397 | - 1. A `static_vector` is a contiguous container that supports constant time 398 | insert and erase operations at the end; insert and erase in the middle take 399 | linear time. Its capacity is part of its type and its elements are stored 400 | within the `static_vector` object itself, meaning that that if `v` is a 401 | `static_vector` then it obeys the identity `&v[n] == &v[0] + n` for all 402 | `0 <= n <= v.size()`. 403 | 404 | - 2. A `static_vector` satisfies the container requirements 405 | (`\ref{container.requirements}`) with the exception of the `swap` member 406 | function, whose complexity is linear instead of constant. It satisfies the 407 | sequence container requirements, including the optional sequence container 408 | requirements (`\ref{sequence.reqmts}`), with the exception of the 409 | `push_front`, `pop_front`, and `emplace_front` member functions, which are not 410 | provided. It satisfies the reversible container 411 | (`\ref{container.requirements}`) and contiguous container 412 | (`\ref{container.requirements.general}`) requirements. Descriptions are 413 | provided here only for operations on `static_vector` that are not described in 414 | one of these tables or for operations where there is additional semantic 415 | information. 416 | 417 | - 3. Class `static_vector` relies on the implicitly-declared special member 418 | functions (`\ref{class.default.ctor}`, `\ref{class.dtor}`, and 419 | `\ref{class.copy.ctor}`) to conform to the container requirements table in 420 | `\ref{container.requirements}`. In addition to the requirements specified in 421 | the container requirements table, the move constructor and move assignment 422 | operator for array require that `T` be `Cpp17MoveConstructible` or 423 | `Cpp17MoveAssignable`, respectively. 424 | 425 | 426 | ```c++ 427 | namespace std { 428 | 429 | template 430 | class static_vector { 431 | public: 432 | // types: 433 | using value_type = T; 434 | using pointer = T*; 435 | using const_pointer = const T*; 436 | using reference = value_type&; 437 | using const_reference = const value_type&; 438 | using size_type = size_t; 439 | using difference_type = ptrdiff_t; 440 | using iterator = implementation-defined; // see [container.requirements] 441 | using const_iterator = implementation-defined; // see [container.requirements] 442 | using reverse_iterator = std::reverse_iterator; 443 | using const_reverse_iterator = std::reverse_iterator; 444 | 445 | // 5.2, copy/move construction: 446 | constexpr static_vector() noexcept; 447 | constexpr static_vector(const static_vector&); 448 | constexpr static_vector(static_vector&&); 449 | constexpr explicit static_vector(size_type n); 450 | constexpr static_vector(size_type n, const value_type& value); 451 | template 452 | constexpr static_vector(InputIterator first, InputIterator last); 453 | constexpr static_vector(initializer_list il); 454 | 455 | // 5.3, copy/move assignment: 456 | constexpr static_vector& operator=(const static_vector& other) 457 | noexcept(is_nothrow_copy_assignable_v); 458 | constexpr static_vector& operator=(static_vector&& other); 459 | noexcept(is_nothrow_move_assignable_v); 460 | template 461 | constexpr void assign(InputIterator first, InputIterator last); 462 | constexpr void assign(size_type n, const value_type& u); 463 | constexpr void assign(initializer_list il); 464 | 465 | // 5.4, destruction 466 | ~static_vector(); 467 | 468 | // iterators 469 | constexpr iterator begin() noexcept; 470 | constexpr const_iterator begin() const noexcept; 471 | constexpr iterator end() noexcept; 472 | constexpr const_iterator end() const noexcept; 473 | constexpr reverse_iterator rbegin() noexcept; 474 | constexpr const_reverse_iterator rbegin() const noexcept; 475 | constexpr reverse_iterator rend() noexcept; 476 | constexpr const_reverse_iterator rend() const noexcept; 477 | constexpr const_iterator cbegin() const noexcept; 478 | constexpr const_iterator cend() const noexcept; 479 | constexpr const_reverse_iterator crbegin() const noexcept; 480 | constexpr const_reverse_iterator crend() const noexcept; 481 | 482 | // 5.5, size/capacity: 483 | constexpr bool empty() const noexcept; 484 | constexpr size_type size() const noexcept; 485 | static constexpr size_type max_size() noexcept; 486 | static constexpr size_type capacity() noexcept; 487 | constexpr void resize(size_type sz); 488 | constexpr void resize(size_type sz, const value_type& c); 489 | 490 | // 5.6, element and data access: 491 | constexpr reference operator[](size_type n); 492 | constexpr const_reference operator[](size_type n) const; 493 | constexpr reference front(); 494 | constexpr const_reference front() const; 495 | constexpr reference back(); 496 | constexpr const_reference back() const; 497 | constexpr T* data() noexcept; 498 | constexpr const T* data() const noexcept; 499 | 500 | // 5.7, modifiers: 501 | constexpr iterator insert(const_iterator position, const value_type& x); 502 | constexpr iterator insert(const_iterator position, value_type&& x); 503 | constexpr iterator insert(const_iterator position, size_type n, const value_type& x); 504 | template 505 | constexpr iterator insert(const_iterator position, InputIterator first, InputIterator last); 506 | constexpr iterator insert(const_iterator position, initializer_list il); 507 | 508 | template 509 | constexpr iterator emplace(const_iterator position, Args&&... args); 510 | template 511 | constexpr reference emplace_back(Args&&... args); 512 | constexpr void push_back(const value_type& x); 513 | constexpr void push_back(value_type&& x); 514 | 515 | constexpr void pop_back(); 516 | constexpr iterator erase(const_iterator position); 517 | constexpr iterator erase(const_iterator first, const_iterator last); 518 | 519 | constexpr void clear() noexcept; 520 | 521 | constexpr void swap(static_vector& x) 522 | noexcept(is_nothrow_swappable_v && 523 | is_nothrow_move_constructible_v); 524 | }; 525 | 526 | template 527 | constexpr bool operator==(const static_vector& a, const static_vector& b); 528 | template 529 | constexpr bool operator!=(const static_vector& a, const static_vector& b); 530 | template 531 | constexpr bool operator<(const static_vector& a, const static_vector& b); 532 | template 533 | constexpr bool operator<=(const static_vector& a, const static_vector& b); 534 | template 535 | constexpr bool operator>(const static_vector& a, const static_vector& b); 536 | template 537 | constexpr bool operator>=(const static_vector& a, const static_vector& b); 538 | 539 | // 5.8, specialized algorithms: 540 | template 541 | constexpr void swap(static_vector& x, static_vector& y) 542 | noexcept(noexcept(x.swap(y))); 543 | 544 | } // namespace std 545 | ``` 546 | 547 | ## 5.2 `static_vector` constructors 548 | 549 | --- 550 | 551 | ```c++ 552 | constexpr static_vector() noexcept; 553 | ``` 554 | 555 | > - _Effects_: Constructs an empty `static_vector`. 556 | > 557 | > - _Ensures_: `empty()`. 558 | > 559 | > - _Complexity_: Constant. 560 | 561 | --- 562 | 563 | ```c++ 564 | constexpr static_vector(static_vector&& rv); 565 | ``` 566 | 567 | > - _Effects_: Constructs a `static_vector` by move-inserting the elements of 568 | > `rv`. 569 | > 570 | > - _Mandates_: `std::is_move_constructivle`. 571 | > 572 | > - _Ensures_: The `static_vector` is equal to the value that `rv` had before 573 | > this construction. 574 | > 575 | > - _Complexity_: Linear in `rv.size()`. 576 | 577 | --- 578 | 579 | ```c++ 580 | constexpr explicit static_vector(size_type n); 581 | ``` 582 | 583 | > - _Effects_: Constructs a `static_vector` with `n` default-inserted elements. 584 | > 585 | > - _Mandates_: `std::is_default_constructible`. 586 | > 587 | > - _Expects_: `n <= capacity()`. 588 | > 589 | > - _Complexity_: Linear in `n`. 590 | 591 | --- 592 | 593 | ```c++ 594 | constexpr static_vector(size_type n, const value_type& value); 595 | ``` 596 | 597 | > - _Effects_: Constructs a `static_vector` with `n` copies of `value`. 598 | > 599 | > - _Mandates_: `std::is_copy_constructible` 600 | > 601 | > - _Expects_: `n <= capacity()`. 602 | > 603 | > - _Complexity_: Linear in `n`. 604 | 605 | --- 606 | 607 | ```c++ 608 | template 609 | constexpr static_vector(InputIterator first, InputIterator last); 610 | ``` 611 | 612 | > - _Effects_: Constructs a `static_vector` equal to the range `[first, last)` 613 | > 614 | > - _Mandates_: `std::is_constructible` 615 | > 616 | > - _Expects_: `distance(first, last) <= capacity()` 617 | > 618 | > - _Complexity_: Linear in `distance(first, last)`. 619 | 620 | ## 5.3 Destruction 621 | 622 | ```c++ 623 | ~static_vector(); 624 | ``` 625 | 626 | > _Effects_: Destroys the `static_vector` and its elements. 627 | > 628 | > _Remarks_: This destructor is trivial if the destructor of `value_type` is 629 | > trivial. 630 | 631 | ## 5.4 Size and capacity 632 | 633 | 634 | ```c++ 635 | static constexpr size_type capacity() noexcept 636 | static constexpr size_type max_size() noexcept 637 | ``` 638 | 639 | > - _Returns_: `N`. 640 | 641 | --- 642 | 643 | ```c++ 644 | constexpr void resize(size_type sz); 645 | ``` 646 | 647 | > - _Effects_: If `sz < size()`, erases the last `size() - sz` elements from the 648 | > sequence. Otherwise, appends `sz - size()` default-constructed elements. 649 | > 650 | > - _Mandates_: `std::is_default_constructible`. 651 | > 652 | > - _Expects_: `sz <= capacity()`. 653 | > 654 | > - _Complexity_: Linear in `sz`. 655 | 656 | --- 657 | 658 | ```c++ 659 | constexpr void resize(size_type sz, const value_type& c); 660 | ``` 661 | 662 | > - _Effects_: If `sz < size()`, erases the last `size() - sz` elements from the 663 | > sequence. Otherwise, appends `sz - size()` copies of `c`. 664 | > 665 | > - _Mandates_: `std::is_copy_constructible`. 666 | > 667 | > - _Expects_: `sz <= capacity()`. 668 | > 669 | > - _Complexity_: Linear in `sz`. 670 | 671 | 672 | ## 5.5 Element and data access 673 | 674 | ```c++ 675 | constexpr T* data() noexcept; 676 | constexpr const T* data() const noexcept; 677 | ``` 678 | 679 | > - _Returns_: A pointer such that `[data(), data() + size())` is a valid range. 680 | > For a non-empty `static_vector`, `data() == addressof(front())`. 681 | > 682 | > - _Complexity_: Constant time. 683 | 684 | ## 5.6 Modifiers 685 | 686 | --- 687 | 688 | Note to LWG: All modifiers have a pre-condition on not exceeding the 689 | `capacity()` when inserting elements. That is, exceeding the `capacity()` of the 690 | vector is undefined behavior. This supports some of the major use cases of this 691 | container (embedded, freestanding, etc.) and was required by the stakeholders 692 | during LEWG review. Currently, this provides maximum freedom to the 693 | implementation to choose an appropriate behavior: `abort`, `assert`, throw an 694 | exception (which exception? `bad_alloc`? `logic_error`? `out_of_bounds`? etc. ). 695 | In the future, this freedom allows us to specify these pre-conditions using 696 | contracts. 697 | 698 | Note to LWG: Because all modifiers have preconditions, they all have narrow 699 | contracts and are not unconditionally `noexcept`. 700 | 701 | --- 702 | 703 | ```c++ 704 | constexpr iterator insert(const_iterator position, const value_type& x); 705 | ``` 706 | 707 | > - _Effects_: Inserts `x` at `position` and invalidates all references to 708 | > elements after `position`. 709 | > 710 | > - _Expects_: `size() < capacity()`. 711 | > 712 | > - _Mandates_: `std::is_copy_constructible`. 713 | > 714 | > - _Complexity_: Linear in `size()`. 715 | > 716 | > - _Remarks_: If an exception is thrown by `value_type`'s copy constructor and 717 | > `is_nothrow_move_constructible_v` is `true` there are no 718 | > effects. Otherwise, if an exception is thrown by `value_type`'s copy 719 | > constructor the effects are _unspecified_. 720 | 721 | --- 722 | 723 | ```c++ 724 | constexpr iterator insert(const_iterator position, size_type n, const value_type& x); 725 | ``` 726 | 727 | > - _Effects_: Inserts `n` copies of `x` at `position` and invalidates all 728 | > references to elements after `position`. 729 | > 730 | > - _Expects_: `n <= capacity() - size()`. 731 | > 732 | > - _Mandates_: `std::is_copy_constructible`. 733 | > 734 | > - _Complexity_: Linear in `size()` and `n`. 735 | > 736 | > - _Remarks_: If an exception is thrown by `value_type`'s copy constructor and 737 | > `is_nothrow_move_constructible_v` is `true` there are no 738 | > effects. Otherwise, if an exception is thrown by `value_type`'s copy 739 | > constructor the effects are _unspecified_. 740 | 741 | --- 742 | 743 | ```c++ 744 | constexpr iterator insert(const_iterator position, value_type&& x); 745 | ``` 746 | 747 | > - _Effects_: Inserts `x` at `position` and invalidates all references to 748 | > elements after `position`. 749 | > 750 | > - _Expects_: `size() < capacity()`. 751 | > 752 | > - _Mandates_: `std::is_move_constructible`. 753 | > 754 | > - _Complexity_: Linear in `size()`. 755 | > 756 | > - _Remarks_: If an exception is thrown by `value_type`'s move constructor the 757 | > effects are _unspecified_. 758 | 759 | --- 760 | 761 | ```c++ 762 | template 763 | constexpr iterator insert(const_iterator position, InputIterator first, InputIterator last); 764 | ``` 765 | 766 | > - _Effects_: Inserts elements in range `[first, last)` at `position` and 767 | > invalidates all references to elements after `position`. 768 | > 769 | > - _Expects_: `distance(first, last) <= capacity() - size()`. 770 | > 771 | > - _Mandates_: `std::is_constructible`. 772 | > 773 | > - _Complexity_: Linear in `size()` and `distance(first, last)`. 774 | > 775 | > - _Remarks_: If an exception is thrown by `value_type` constructor from 776 | > `decltype(*first)` and `is_nothrow_move_constructible_v` is 777 | > `true` there are no effects. Otherwise, if an exception is thrown by 778 | > `value_type`'s constructor from `decltype(*first)` the effects are 779 | > _unspecified_. 780 | 781 | --- 782 | 783 | ```c++ 784 | constexpr iterator insert(const_iterator position, initializer_list il); 785 | ``` 786 | 787 | > - _Effects_: Inserts elements of `il` at `position` and invalidates all 788 | > references to elements after `position`. 789 | > 790 | > - _Expects_: `il.size() <= capacity() - size()`. 791 | > 792 | > - _Mandates_: `std::is_copy_constructible`. 793 | > 794 | > - _Complexity_: Linear in `size()` and `il.size()`. 795 | > 796 | > - _Remarks_: If an exception is thrown by `value_type`'s copy constructor and 797 | > `is_nothrow_move_constructible_v` is `true` there are no 798 | > effects. Otherwise, if an exception is thrown by `value_type`'s copy 799 | > constructor the effects are _unspecified_. 800 | 801 | --- 802 | 803 | ```c++ 804 | template 805 | constexpr iterator emplace(const_iterator position, Args&&... args); 806 | ``` 807 | 808 | > - _Effects_: Inserts an element constructed from `args...` at `position` and 809 | > invalidates all references to elements after `position`. 810 | > 811 | > - _Expects_: `size() < capacity()`. 812 | > 813 | > - _Mandates_: `std::is_constructible`. 814 | > 815 | > - _Complexity_: Linear in `size()`. 816 | > 817 | > - _Remarks_: If an exception is thrown by `value_type`'s constructor from 818 | > `args...` and `is_nothrow_move_constructible_v` is `true` there 819 | > are no effects. Otherwise, if an exception is thrown by `value_type`'s 820 | > constructor from `args...` the effects are _unspecified_. 821 | 822 | --- 823 | 824 | ```c++ 825 | template 826 | constexpr reference emplace_back(Args&&... args); 827 | ``` 828 | 829 | > - _Effects_: Inserts an element constructed from `args...` at the end. 830 | > 831 | > - _Expects_: `size() < capacity()`. 832 | > 833 | > - _Mandates_: `std::is_constructible`. 834 | > 835 | > - _Complexity_: Constant. 836 | > 837 | > - _Remarks_: If an exception is thrown by `value_type`'s constructor from 838 | > `args...` there are no effects. 839 | 840 | --- 841 | 842 | ```c++ 843 | constexpr void push_back(const value_type& x); 844 | ``` 845 | 846 | > - _Effects_: Inserts a copy of `x` at the end. 847 | > 848 | > - _Expects_: `size() < capacity()`. 849 | > 850 | > - _Mandates_: `std::is_copy_constructible`. 851 | > 852 | > - _Complexity_: Constant. 853 | > 854 | > - _Remarks_: If an exception is thrown by `value_type`'s copy constructor 855 | > there are no effects. 856 | 857 | --- 858 | 859 | ```c++ 860 | constexpr void push_back(value_type&& x); 861 | ``` 862 | 863 | > - _Effects_: Moves `x` to the end. 864 | > 865 | > - _Expects_: `size() < capacity()`. 866 | > 867 | > - _Mandates_: `std::is_move_constructible`. 868 | > 869 | > - _Complexity_: Constant. 870 | > 871 | > - _Remarks_: If an exception is thrown by `value_type`'s move constructor 872 | > there are no effects. 873 | 874 | --- 875 | 876 | 877 | ```c++ 878 | constexpr void pop_back(); 879 | ``` 880 | 881 | > - _Effects_: Removes the last element of the container and destroys it. 882 | > 883 | > - _Expects_: `!empty()`. 884 | > 885 | > - _Complexity_: Constant. 886 | 887 | --- 888 | 889 | ```c++ 890 | constexpr iterator erase(const_iterator position); 891 | ``` 892 | 893 | > - _Effects_: Removes the element at `position`, destroys it, and invalidates 894 | > references to elements after `position`. 895 | > 896 | > - _Expects_: `position` in range `[begin(), end())`. 897 | > 898 | > - _Complexity_: Linear in `size()`. 899 | > 900 | > - _Remarks_: If an exception is thrown by `value_type`'s move constructor 901 | > the effects are _unspecified_. 902 | 903 | --- 904 | 905 | ```c++ 906 | constexpr iterator erase(const_iterator first, const_iterator last); 907 | ``` 908 | 909 | > - _Effects_: Removes the elements in range `[first, last)`, destroying them, 910 | > and invalidating references to elements after `last`. 911 | > 912 | > - _Expects_: `[first, last)` in range `[begin(), end())`. 913 | > 914 | > - _Complexity_: Linear in `size()` and `distance(first, last)`. 915 | > 916 | > - _Remarks_: If an exception is thrown by `value_type`'s move constructor 917 | > the effects are _unspecified_. 918 | 919 | --- 920 | 921 | ```c++ 922 | constexpr void swap(static_vector& x) 923 | noexcept(is_nothrow_swappable_v && 924 | is_nothrow_move_constructible_v); 925 | ``` 926 | 927 | > - _Effects_: Exchanges the contents of `*this` with `x`. All references to the 928 | > elements of `*this` and `x` are invalidated. 929 | > 930 | > - _Complexity_: Linear in `size()` and `x.size()`. 931 | > 932 | > - _Remarks_: Shall not participate in overload resolution unless 933 | > `is_move_constructible_v` is `true` and 934 | > `is_swappable_v` is `true` 935 | 936 | ## 5.7 `static_vector` specialized algorithms 937 | 938 | ```c++ 939 | template 940 | constexpr void swap(static_vector& x, 941 | static_vector& y) 942 | noexcept(noexcept(x.swap(y))); 943 | ``` 944 | 945 | > - _Constraints_: This function shall not participate in overload resolution 946 | > unless `is_swappable_v` is `true`. 947 | > 948 | > - _Effects_: As if by `x.swap(y)`. 949 | > 950 | > - _Complexity_: Linear in `size()` and `x.size()`. 951 | 952 | # 6. Acknowledgments 953 | 954 | The following people have significantly contributed to the development of this 955 | proposal. This proposal is based on Boost.Container's 956 | `boost::container::static_vector` and my extensive usage of this class over the 957 | years. As a consequence the authors of Boost.Container (Adam Wulkiewicz, Andrew 958 | Hundt, and Ion Gaztanaga) have had a very significant indirect impact on this 959 | proposal. The implementation of libc++ `std::vector` and the libc++ test-suite 960 | have been used extensively while prototyping this proposal, such that its 961 | author, Howard Hinnant, has had a significant indirect impact on the result of 962 | this proposal as well. The following people provided valuable feedback that 963 | influenced some aspects of this proposal: Walter Brown, Zach Laine, Rein 964 | Halbersma, and Andrzej Krzemieński. But I want to wholeheartedly acknowledge 965 | Casey Carter for taking the time to do a very detailed analysis of the whole 966 | proposal, which was invaluable and reshaped it in fundamental ways. 967 | 968 | # 7. References 969 | 970 | - [1] [Boost.Container::static_vector][boost_static_vector]: http://www.boost.org/doc/libs/1_59_0/doc/html/boost/container/static_vector.html . 971 | - [2] [EASTL fixed_vector][eastl]: https://github.com/questor/eastl/blob/master/fixed_vector.h#L71 . 972 | - [3] [Folly small_vector][folly]: https://github.com/facebook/folly/blob/master/folly/docs/small_vector.md . 973 | - [4] [Howard Hinnant's stack_alloc][stack_alloc]: https://howardhinnant.github.io/stack_alloc.html . 974 | - [5] [P0494R0: `contiguous_container` proposal][contiguous_container]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0494r0.pdf 975 | - [6] [P0597R0: `std::constexpr_vector`][constexpr_vector_1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0597r0.html 976 | - [7] [P0639R0: Changing attack vector of the `constexpr_vector`][constexpr_vector_2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0639r0.html . 977 | - [8] [PR0274: Clump – A Vector-like Contiguous Sequence Container with Embedded Storage][clump]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0274r0.pdf 978 | - [9] [Boost.Container::small_vector][boostsmallvector]: http://www.boost.org/doc/libs/master/doc/html/boost/container/small_vector.html. 979 | - [10] [LLVM small_vector][llvmsmallvector]: http://llvm.org/docs/doxygen/html/classllvm_1_1SmallVector.html . 980 | - [11] [EASTL design][eastldesign]: https://github.com/questor/eastl/blob/master/doc/EASTL%20Design.html#L284 . 981 | - [12] [Interest in StaticVector - fixed capacity vector](https:>>groups.google.com>d>topic>boost-developers-archive>4n1QuJyKTTk>discussion): https://groups.google.com/d/topic/boost-developers-archive/4n1QuJyKTTk/discussion . 982 | - [13] [Stack-based vector container](https:>>groups.google.com>d>topic>boost-developers-archive>9BEXjV8ZMeQ>discussion): https://groups.google.com/d/topic/boost-developers-archive/9BEXjV8ZMeQ/discussion. 983 | - [14] [static_vector: fixed capacity vector update](https:>>groups.google.com>d>topic>boost-developers-archive>d5_Kp-nmW6c>discussion): https://groups.google.com/d/topic/boost-developers-archive/d5_Kp-nmW6c/discussion. 984 | 985 | 986 | [stack_alloc]: https://howardhinnant.github.io/stack_alloc.html 987 | [fixed_capacity_vector]: http://github.com/gnzlbg/fixed_capacity_vector 988 | [boost_static_vector]: http://www.boost.org/doc/libs/1_59_0/doc/html/boost/container/static_vector.html 989 | [travis-shield]: https://img.shields.io/travis/gnzlbg/embedded_vector.svg?style=flat-square 990 | [travis]: https://travis-ci.org/gnzlbg/embedded_vector 991 | [coveralls-shield]: https://img.shields.io/coveralls/gnzlbg/embedded_vector.svg?style=flat-square 992 | [coveralls]: https://coveralls.io/github/gnzlbg/embedded_vector 993 | [docs-shield]: https://img.shields.io/badge/docs-online-blue.svg?style=flat-square 994 | [docs]: https://gnzlbg.github.io/embedded_vector 995 | [folly]: https://github.com/facebook/folly/blob/master/folly/docs/small_vector.md 996 | [eastl]: https://github.com/questor/eastl/blob/master/fixed_vector.h#L71 997 | [eastldesign]: https://github.com/questor/eastl/blob/master/doc/EASTL%20Design.html#L284 998 | [clump]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0274r0.pdf 999 | [boostsmallvector]: http://www.boost.org/doc/libs/master/doc/html/boost/container/small_vector.html 1000 | [llvmsmallvector]: http://llvm.org/docs/doxygen/html/classllvm_1_1SmallVector.html 1001 | [contiguous_container]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0494r0.pdf 1002 | [constexpr_vector_1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0597r0.html 1003 | [constexpr_vector_2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0639r0.html 1004 | -------------------------------------------------------------------------------- /include/experimental/fixed_capacity_vector: -------------------------------------------------------------------------------- 1 | #ifndef STD_EXPERIMENTAL_FIXED_CAPACITY_VECTOR 2 | #define STD_EXPERIMENTAL_FIXED_CAPACITY_VECTOR 3 | /// \file 4 | /// 5 | /// Dynamically-resizable vector with fixed-capacity. 6 | /// 7 | /// Copyright Gonzalo Brito Gadeschi 2015-2017 8 | /// Copyright Eric Niebler 2013-2014 9 | /// Copyright Casey Carter 2016 10 | /// 11 | /// This file is released under the Boost Software License: 12 | // 13 | // Boost Software License - Version 1.0 - August 17th, 2003 14 | // 15 | // Permission is hereby granted, free of charge, to any person or organization 16 | // obtaining a copy of the software and accompanying documentation covered by 17 | // this license (the "Software") to use, reproduce, display, distribute, 18 | // execute, and transmit the Software, and to prepare derivative works of the 19 | // Software, and to permit third-parties to whom the Software is furnished to 20 | // do so, all subject to the following: 21 | // 22 | // The copyright notices in the Software and this entire statement, including 23 | // the above license grant, this restriction and the following disclaimer, 24 | // must be included in all copies of the Software, in whole or in part, and 25 | // all derivative works of the Software, unless such copies or derivative 26 | // works are solely in the form of machine-executable object code generated by 27 | // a source language processor. 28 | // 29 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 32 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 33 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 34 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | // DEALINGS IN THE SOFTWARE. 36 | // 37 | // Some of the code has been adapted from the range-v3 library: 38 | // 39 | // https://github.com/ericniebler/range-v3/ 40 | // 41 | // which is also under the Boost Software license. 42 | // 43 | // Some of the code has been adapted from libc++: 44 | // 45 | // and is annotated with "adapted from libc++" below, and is thus under the 46 | // following license: 47 | // 48 | //===----------------------------------------------------------------------===// 49 | // 50 | // The LLVM Compiler Infrastructure 51 | // 52 | // This file is dual licensed under the MIT and the University of Illinois Open 53 | // Source Licenses. See LICENSE.TXT for details. 54 | // 55 | //===----------------------------------------------------------------------===// 56 | // 57 | #include 58 | #include // for size_t 59 | #include // for fixed-width integer types 60 | #include // for less and equal_to 61 | #include // for reverse_iterator and iterator traits 62 | #include // for numeric_limits 63 | #include // for length_error 64 | #include // for aligned_storage and all meta-functions 65 | #include // for assertion diagnostics 66 | 67 | /// Unreachable code 68 | #define FCV_UNREACHABLE __builtin_unreachable() 69 | 70 | /// Optimizer allowed to assume that EXPR evaluates to true 71 | #define FCV_ASSUME(EXPR) \ 72 | static_cast((EXPR) ? void(0) : __builtin_unreachable()) 73 | 74 | /// Assert pretty printer 75 | #define FCV_ASSERT(...) \ 76 | static_cast((__VA_ARGS__) \ 77 | ? void(0) \ 78 | : ::std::experimental::fcv_detail::assert_failure( \ 79 | static_cast(__FILE__), __LINE__, \ 80 | "assertion failed: " #__VA_ARGS__)) 81 | 82 | /// Likely/unlikely branches 83 | #define FCV_LIKELY(boolean_expr) __builtin_expect(!!(boolean_expr), 1) 84 | #define FCV_UNLIKELY(boolean_expr) __builtin_expect(!!(boolean_expr), 0) 85 | 86 | /// Expect asserts the condition in debug builds and assumes the condition to be 87 | /// true in release builds. 88 | #ifdef NDEBUG 89 | #define FCV_EXPECT(EXPR) FCV_ASSUME(EXPR) 90 | #else 91 | #define FCV_EXPECT(EXPR) FCV_ASSERT(EXPR) 92 | #endif 93 | 94 | #define FCV_CONCEPT_PP_CAT_(X, Y) X##Y 95 | #define FCV_CONCEPT_PP_CAT(X, Y) FCV_CONCEPT_PP_CAT_(X, Y) 96 | 97 | /// Requires-clause emulation with SFINAE (for templates) 98 | #define FCV_REQUIRES_(...) \ 99 | int FCV_CONCEPT_PP_CAT(_concept_requires_, __LINE__) \ 100 | = 42, \ 101 | typename ::std::enable_if \ 102 | < (FCV_CONCEPT_PP_CAT(_concept_requires_, __LINE__) == 43) \ 103 | || (__VA_ARGS__), \ 104 | int > ::type = 0 /**/ 105 | 106 | /// Requires-clause emulation with SFINAE (for "non-templates") 107 | #define FCV_REQUIRES(...) \ 108 | template ::type \ 113 | = 0> /**/ 114 | 115 | namespace std 116 | { 117 | namespace experimental 118 | { 119 | // Private utilites (each std lib should already have this) 120 | namespace fcv_detail 121 | { 122 | /// \name Utilities 123 | ///@{ 124 | 125 | template 126 | [[noreturn]] void assert_failure(char const* file, int line, 127 | char const* msg) { 128 | fprintf(stderr, "%s(%d): %s\n", file, line, msg); 129 | abort(); 130 | } 131 | 132 | template 133 | using bool_ = integral_constant; 134 | 135 | /// \name Concepts (poor-man emulation using type traits) 136 | ///@{ 137 | template 138 | static constexpr bool Constructible 139 | = is_constructible_v; 140 | 141 | template 142 | static constexpr bool CopyConstructible 143 | = is_copy_constructible_v; 144 | 145 | template 146 | static constexpr bool MoveConstructible 147 | = is_move_constructible_v; 148 | 149 | template 150 | static constexpr bool Assignable = is_assignable_v; 151 | 152 | template 153 | static constexpr bool Movable = is_object_v&& Assignable&& 154 | MoveConstructible&& is_swappable_v; 155 | 156 | template 157 | static constexpr bool Convertible = is_convertible_v; 158 | 159 | template 160 | static constexpr bool Trivial = is_trivial_v; 161 | 162 | template 163 | static constexpr bool Const = is_const_v; 164 | 165 | template 166 | static constexpr bool Pointer = is_pointer_v; 167 | ///@} // Concepts 168 | 169 | template 170 | using range_iterator_t = decltype(begin(declval())); 171 | 172 | template 173 | using iterator_reference_t = typename iterator_traits::reference; 174 | 175 | template 176 | using iterator_category_t = 177 | typename iterator_traits::iterator_category; 178 | 179 | template 180 | struct Iterator_ : false_type 181 | { 182 | }; 183 | 184 | template 185 | struct Iterator_>> 186 | : bool_, Cat>> 187 | { 188 | }; 189 | 190 | /// \name Concepts (poor-man emulation using type traits) 191 | ///@{ 192 | template 193 | static constexpr bool InputIterator 194 | = Iterator_{}; 195 | 196 | template 197 | static constexpr bool ForwardIterator 198 | = Iterator_{}; 199 | 200 | template 201 | static constexpr bool OutputIterator 202 | = Iterator_{} || ForwardIterator; 203 | 204 | template 205 | static constexpr bool BidirectionalIterator 206 | = Iterator_{}; 207 | 208 | template 209 | static constexpr bool RandomAccessIterator 210 | = Iterator_{}; 211 | 212 | template 213 | static constexpr bool RandomAccessRange 214 | = RandomAccessIterator>; 215 | ///@} // Concepts 216 | 217 | // clang-format off 218 | 219 | /// Smallest fixed-width unsigned integer type that can represent 220 | /// values in the range [0, N]. 221 | template 222 | using smallest_size_t 223 | = conditional_t<(N < numeric_limits::max()), uint8_t, 224 | conditional_t<(N < numeric_limits::max()), uint16_t, 225 | conditional_t<(N < numeric_limits::max()), uint32_t, 226 | conditional_t<(N < numeric_limits::max()), uint64_t, 227 | size_t>>>>; 228 | // clang-format on 229 | 230 | /// Index a range doing bound checks in debug builds 231 | template )> 233 | constexpr decltype(auto) index(Rng&& rng, Index&& i) noexcept 234 | { 235 | FCV_EXPECT(static_cast(i) < (end(rng) - begin(rng))); 236 | return begin(forward(rng))[forward(i)]; 237 | } 238 | 239 | /// \name Workarounds 240 | ///@{ 241 | 242 | // WORKAROUND: std::rotate is not constexpr 243 | template )> 245 | constexpr void slow_rotate(ForwardIt first, ForwardIt n_first, 246 | ForwardIt last) 247 | { 248 | ForwardIt next = n_first; 249 | while (first != next) 250 | { 251 | swap(*(first++), *(next++)); 252 | if (next == last) 253 | { 254 | next = n_first; 255 | } 256 | else if (first == n_first) 257 | { 258 | n_first = next; 259 | } 260 | } 261 | } 262 | 263 | // WORKAROUND: std::move is not constexpr 264 | template && OutputIterator)> 267 | constexpr OutputIt move(InputIt b, InputIt e, OutputIt to) 268 | { 269 | for (; b != e; ++b, (void)++to) 270 | { 271 | *to = ::std::move(*b); 272 | } 273 | return to; 274 | } 275 | 276 | // WORKAROUND: std::equal is not constexpr 277 | template && 280 | InputIterator)> 281 | constexpr bool cmp(InputIterator1 first1, InputIterator1 last1, 282 | InputIterator2 first2, InputIterator2 last2, 283 | BinaryPredicate pred) 284 | { 285 | for (; first1 != last1 && first2 != last2; 286 | ++first1, (void)++first2) 287 | { 288 | if (!pred(*first1, *first2)) 289 | { 290 | return false; 291 | } 292 | } 293 | return first1 == last1 && first2 == last2; 294 | } 295 | 296 | ///@} // Workarounds 297 | 298 | ///@} // Utilities 299 | 300 | /// Types implementing the `fixed_capactiy_vector`'s storage 301 | namespace storage 302 | { 303 | /// Storage for zero elements. 304 | template 305 | struct zero_sized 306 | { 307 | using size_type = uint8_t; 308 | using value_type = T; 309 | using difference_type = ptrdiff_t; 310 | using pointer = T*; 311 | using const_pointer = T const*; 312 | 313 | /// Pointer to the data in the storage. 314 | static constexpr pointer data() noexcept 315 | { 316 | return nullptr; 317 | } 318 | /// Number of elements currently stored. 319 | static constexpr size_type size() noexcept 320 | { 321 | return 0; 322 | } 323 | /// Capacity of the storage. 324 | static constexpr size_type capacity() noexcept 325 | { 326 | return 0; 327 | } 328 | /// Is the storage empty? 329 | static constexpr bool empty() noexcept 330 | { 331 | return true; 332 | } 333 | /// Is the storage full? 334 | static constexpr bool full() noexcept 335 | { 336 | return true; 337 | } 338 | 339 | /// Constructs a new element at the end of the storage 340 | /// in-place. 341 | /// 342 | /// Increases size of the storage by one. 343 | /// Always fails for empty storage. 344 | template )> 346 | static constexpr void emplace_back(Args&&...) noexcept 347 | { 348 | FCV_EXPECT(false 349 | && "tried to emplace_back on empty storage"); 350 | } 351 | /// Removes the last element of the storage. 352 | /// Always fails for empty storage. 353 | static constexpr void pop_back() noexcept 354 | { 355 | FCV_EXPECT(false 356 | && "tried to pop_back on empty storage"); 357 | } 358 | /// Changes the size of the storage without adding or 359 | /// removing elements (unsafe). 360 | /// 361 | /// The size of an empty storage can only be changed to 0. 362 | static constexpr void unsafe_set_size( 363 | size_t new_size) noexcept 364 | { 365 | FCV_EXPECT( 366 | new_size == 0 367 | && "tried to change size of empty storage to " 368 | "non-zero value"); 369 | } 370 | 371 | /// Destroys all elements of the storage in range [begin, 372 | /// end) without changings its size (unsafe). 373 | /// 374 | /// Nothing to destroy since the storage is empty. 375 | template )> 377 | static constexpr void unsafe_destroy( 378 | InputIt /* begin */, InputIt /* end */) noexcept 379 | { 380 | } 381 | 382 | /// Destroys all elements of the storage without changing 383 | /// its size (unsafe). 384 | /// 385 | /// Nothing to destroy since the storage is empty. 386 | static constexpr void unsafe_destroy_all() noexcept 387 | { 388 | } 389 | 390 | constexpr zero_sized() = default; 391 | constexpr zero_sized(zero_sized const&) = default; 392 | constexpr zero_sized& operator =(zero_sized const&) 393 | = default; 394 | constexpr zero_sized(zero_sized&&) = default; 395 | constexpr zero_sized& operator=(zero_sized&&) = default; 396 | ~zero_sized() = default; 397 | 398 | /// Constructs an empty storage from an initializer list of 399 | /// zero elements. 400 | template )> 401 | constexpr zero_sized(initializer_list il) noexcept 402 | { 403 | FCV_EXPECT( 404 | il.size() == 0 405 | && "tried to construct storage::empty from a " 406 | "non-empty initializer list"); 407 | } 408 | }; 409 | 410 | /// Storage for trivial types. 411 | template 412 | struct trivial 413 | { 414 | static_assert(Trivial, 415 | "storage::trivial requires Trivial"); 416 | static_assert(Capacity != size_t{0}, 417 | "Capacity must be greater " 418 | "than zero (use " 419 | "storage::zero_sized instead)"); 420 | 421 | using size_type = smallest_size_t; 422 | using value_type = T; 423 | using difference_type = ptrdiff_t; 424 | using pointer = T*; 425 | using const_pointer = T const*; 426 | 427 | private: 428 | // If the value_type is const, make a const array of 429 | // non-const elements: 430 | using data_t = conditional_t< 431 | !Const, array, 432 | const array, Capacity>>; 433 | alignas(alignof(T)) data_t data_{}; 434 | 435 | /// Number of elements allocated in the storage: 436 | size_type size_ = 0; 437 | 438 | public: 439 | /// Direct access to the underlying storage. 440 | /// 441 | /// Complexity: O(1) in time and space. 442 | constexpr const_pointer data() const noexcept 443 | { 444 | return data_.data(); 445 | } 446 | 447 | /// Direct access to the underlying storage. 448 | /// 449 | /// Complexity: O(1) in time and space. 450 | constexpr pointer data() noexcept 451 | { 452 | return data_.data(); 453 | } 454 | 455 | /// Number of elements in the storage. 456 | /// 457 | /// Complexity: O(1) in time and space. 458 | constexpr size_type size() const noexcept 459 | { 460 | return size_; 461 | } 462 | 463 | /// Maximum number of elements that can be allocated in the 464 | /// storage. 465 | /// 466 | /// Complexity: O(1) in time and space. 467 | static constexpr size_type capacity() noexcept 468 | { 469 | return Capacity; 470 | } 471 | 472 | /// Is the storage empty? 473 | constexpr bool empty() const noexcept 474 | { 475 | return size() == size_type{0}; 476 | } 477 | 478 | /// Is the storage full? 479 | constexpr bool full() const noexcept 480 | { 481 | return size() == Capacity; 482 | } 483 | 484 | /// Constructs an element in-place at the end of the 485 | /// storage. 486 | /// 487 | /// Complexity: O(1) in time and space. 488 | /// Contract: the storage is not full. 489 | template and 491 | Assignable)> 492 | constexpr void emplace_back(Args&&... args) noexcept 493 | { 494 | FCV_EXPECT(!full() 495 | && "tried to emplace_back on full storage!"); 496 | index(data_, size()) = T(forward(args)...); 497 | unsafe_set_size(size() + 1); 498 | } 499 | 500 | /// Remove the last element from the container. 501 | /// 502 | /// Complexity: O(1) in time and space. 503 | /// Contract: the storage is not empty. 504 | constexpr void pop_back() noexcept 505 | { 506 | FCV_EXPECT(!empty() 507 | && "tried to pop_back from empty storage!"); 508 | unsafe_set_size(size() - 1); 509 | } 510 | 511 | /// (unsafe) Changes the container size to \p new_size. 512 | /// 513 | /// Contract: `new_size <= capacity()`. 514 | /// \warning No elements are constructed or destroyed. 515 | constexpr void unsafe_set_size(size_t new_size) noexcept 516 | { 517 | FCV_EXPECT(new_size <= Capacity 518 | && "new_size out-of-bounds [0, Capacity]"); 519 | size_ = size_type(new_size); 520 | } 521 | 522 | /// (unsafe) Destroy elements in the range [begin, end). 523 | /// 524 | /// \warning: The size of the storage is not changed. 525 | template )> 527 | constexpr void unsafe_destroy(InputIt, InputIt) noexcept 528 | { 529 | } 530 | 531 | /// (unsafe) Destroys all elements of the storage. 532 | /// 533 | /// \warning: The size of the storage is not changed. 534 | static constexpr void unsafe_destroy_all() noexcept 535 | { 536 | } 537 | 538 | constexpr trivial() noexcept = default; 539 | constexpr trivial(trivial const&) noexcept = default; 540 | constexpr trivial& operator=(trivial const&) noexcept 541 | = default; 542 | constexpr trivial(trivial&&) noexcept = default; 543 | constexpr trivial& operator=(trivial&&) noexcept = default; 544 | ~trivial() = default; 545 | 546 | private: 547 | template )> 548 | static constexpr array, Capacity> 549 | unsafe_recast_init_list(initializer_list& il) noexcept 550 | { 551 | FCV_EXPECT( 552 | il.size() <= capacity() 553 | && "trying to construct storage from an " 554 | "initializer_list " 555 | "whose size exceeds the storage capacity"); 556 | array, Capacity> d_{}; 557 | for (size_t i = 0, e = il.size(); i < e; ++i) 558 | { 559 | index(d_, i) = index(il, i); 560 | } 561 | return d_; 562 | } 563 | 564 | public: 565 | /// Constructor from initializer list. 566 | /// 567 | /// Contract: `il.size() <= capacity()`. 568 | template )> 569 | constexpr trivial(initializer_list il) noexcept 570 | : data_(unsafe_recast_init_list(il)) 571 | { 572 | unsafe_set_size(static_cast(il.size())); 573 | } 574 | }; 575 | 576 | /// Storage for non-trivial elements. 577 | template 578 | struct non_trivial 579 | { 580 | static_assert( 581 | !Trivial, 582 | "use storage::trivial for Trivial elements"); 583 | static_assert(Capacity != size_t{0}, 584 | "Capacity must be greater than zero!"); 585 | 586 | /// Smallest size_type that can represent Capacity: 587 | using size_type = smallest_size_t; 588 | using value_type = T; 589 | using difference_type = ptrdiff_t; 590 | using pointer = T*; 591 | using const_pointer = T const*; 592 | 593 | private: 594 | /// Number of elements allocated in the embedded storage: 595 | size_type size_ = 0; 596 | 597 | using aligned_storage_t 598 | = aligned_storage_t), 599 | alignof(remove_const_t)>; 600 | using data_t = conditional_t, aligned_storage_t, 601 | const aligned_storage_t>; 602 | alignas(alignof(T)) data_t data_[Capacity]{}; 603 | // FIXME: ^ this won't work for types with "broken" alignof 604 | // like SIMD types (one would also need to provide an 605 | // overload of operator new to make heap allocations of this 606 | // type work for these types). 607 | 608 | public: 609 | /// Direct access to the underlying storage. 610 | /// 611 | /// Complexity: O(1) in time and space. 612 | const_pointer data() const noexcept 613 | { 614 | return reinterpret_cast(data_); 615 | } 616 | 617 | /// Direct access to the underlying storage. 618 | /// 619 | /// Complexity: O(1) in time and space. 620 | pointer data() noexcept 621 | { 622 | return reinterpret_cast(data_); 623 | } 624 | 625 | /// Pointer to one-past-the-end. 626 | const_pointer end() const noexcept 627 | { 628 | return data() + size(); 629 | } 630 | 631 | /// Pointer to one-past-the-end. 632 | pointer end() noexcept 633 | { 634 | return data() + size(); 635 | } 636 | 637 | /// Number of elements in the storage. 638 | /// 639 | /// Complexity: O(1) in time and space. 640 | constexpr size_type size() const noexcept 641 | { 642 | return size_; 643 | } 644 | 645 | /// Maximum number of elements that can be allocated in the 646 | /// storage. 647 | /// 648 | /// Complexity: O(1) in time and space. 649 | static constexpr size_type capacity() noexcept 650 | { 651 | return Capacity; 652 | } 653 | 654 | /// Is the storage empty? 655 | constexpr bool empty() const noexcept 656 | { 657 | return size() == size_type{0}; 658 | } 659 | 660 | /// Is the storage full? 661 | constexpr bool full() const noexcept 662 | { 663 | return size() == Capacity; 664 | } 665 | 666 | /// Constructs an element in-place at the end of the 667 | /// embedded storage. 668 | /// 669 | /// Complexity: O(1) in time and space. 670 | /// Contract: the storage is not full. 671 | template )> 673 | void emplace_back(Args&&... args) noexcept( 674 | noexcept(new (end()) T(forward(args)...))) 675 | { 676 | FCV_EXPECT(!full() 677 | && "tried to emplace_back on full storage"); 678 | new (end()) T(forward(args)...); 679 | unsafe_set_size(size() + 1); 680 | } 681 | 682 | /// Remove the last element from the container. 683 | /// 684 | /// Complexity: O(1) in time and space. 685 | /// Contract: the storage is not empty. 686 | void pop_back() noexcept(is_nothrow_destructible_v) 687 | { 688 | FCV_EXPECT(!empty() 689 | && "tried to pop_back from empty storage!"); 690 | auto ptr = end() - 1; 691 | ptr->~T(); 692 | unsafe_set_size(size() - 1); 693 | } 694 | 695 | /// (unsafe) Changes the container size to \p new_size. 696 | /// 697 | /// Contract: `new_size <= capacity()`. 698 | /// \warning No elements are constructed or destroyed. 699 | constexpr void unsafe_set_size(size_t new_size) noexcept 700 | { 701 | FCV_EXPECT(new_size <= Capacity 702 | && "new_size out-of-bounds [0, Capacity)"); 703 | size_ = size_type(new_size); 704 | } 705 | 706 | /// (unsafe) Destroy elements in the range [begin, end). 707 | /// 708 | /// \warning: The size of the storage is not changed. 709 | template )> 711 | void unsafe_destroy(InputIt first, InputIt last) noexcept( 712 | is_nothrow_destructible_v) 713 | { 714 | FCV_EXPECT(first >= data() && first <= end() 715 | && "first is out-of-bounds"); 716 | FCV_EXPECT(last >= data() && last <= end() 717 | && "last is out-of-bounds"); 718 | for (; first != last; ++first) 719 | { 720 | first->~T(); 721 | } 722 | } 723 | 724 | /// (unsafe) Destroys all elements of the storage. 725 | /// 726 | /// \warning: The size of the storage is not changed. 727 | void unsafe_destroy_all() noexcept( 728 | is_nothrow_destructible_v) 729 | { 730 | unsafe_destroy(data(), end()); 731 | } 732 | 733 | constexpr non_trivial() = default; 734 | constexpr non_trivial(non_trivial const&) = default; 735 | constexpr non_trivial& operator=(non_trivial const&) 736 | = default; 737 | constexpr non_trivial(non_trivial&&) = default; 738 | constexpr non_trivial& operator=(non_trivial&&) = default; 739 | ~non_trivial() noexcept(is_nothrow_destructible_v) 740 | { 741 | unsafe_destroy_all(); 742 | } 743 | 744 | /// Constructor from initializer list. 745 | /// 746 | /// Contract: `il.size() <= capacity()`. 747 | template )> 748 | constexpr non_trivial(initializer_list il) noexcept( 749 | noexcept(emplace_back(index(il, 0)))) 750 | { 751 | FCV_EXPECT( 752 | il.size() <= capacity() 753 | && "trying to construct storage from an " 754 | "initializer_list " 755 | "whose size exceeds the storage capacity"); 756 | for (size_t i = 0; i < il.size(); ++i) 757 | { 758 | emplace_back(index(il, i)); 759 | } 760 | } 761 | }; 762 | 763 | /// Selects the vector storage. 764 | template 765 | using _t = conditional_t< 766 | Capacity == 0, zero_sized, 767 | conditional_t, trivial, 768 | non_trivial>>; 769 | 770 | } // namespace storage 771 | 772 | } // namespace fcv_detail 773 | 774 | /// Dynamically-resizable fixed-capacity vector. 775 | template 776 | struct fixed_capacity_vector 777 | : private fcv_detail::storage::_t 778 | { 779 | private: 780 | static_assert(is_nothrow_destructible_v, 781 | "T must be nothrow destructible"); 782 | using base_t = fcv_detail::storage::_t; 783 | using self = fixed_capacity_vector; 784 | 785 | using base_t::unsafe_destroy; 786 | using base_t::unsafe_destroy_all; 787 | using base_t::unsafe_set_size; 788 | 789 | public: 790 | using value_type = typename base_t::value_type; 791 | using difference_type = ptrdiff_t; 792 | using reference = value_type&; 793 | using const_reference = value_type const&; 794 | using pointer = typename base_t::pointer; 795 | using const_pointer = typename base_t::const_pointer; 796 | using iterator = typename base_t::pointer; 797 | using const_iterator = typename base_t::const_pointer; 798 | using size_type = size_t; 799 | using reverse_iterator = ::std::reverse_iterator; 800 | using const_reverse_iterator 801 | = ::std::reverse_iterator; 802 | 803 | /// \name Size / capacity 804 | ///@{ 805 | using base_t::empty; 806 | using base_t::full; 807 | 808 | /// Number of elements in the vector 809 | constexpr size_type size() const noexcept 810 | { 811 | return base_t::size(); 812 | } 813 | 814 | /// Maximum number of elements that can be allocated in the vector 815 | static constexpr size_type capacity() noexcept 816 | { 817 | return base_t::capacity(); 818 | } 819 | 820 | /// Maximum number of elements that can be allocated in the vector 821 | static constexpr size_type max_size() noexcept 822 | { 823 | return capacity(); 824 | } 825 | 826 | ///@} // Size / capacity 827 | 828 | /// \name Data access 829 | ///@{ 830 | 831 | using base_t::data; 832 | 833 | ///@} // Data access 834 | 835 | /// \name Iterators 836 | ///@{ 837 | 838 | constexpr iterator begin() noexcept 839 | { 840 | return data(); 841 | } 842 | constexpr const_iterator begin() const noexcept 843 | { 844 | return data(); 845 | } 846 | constexpr iterator end() noexcept 847 | { 848 | return data() + size(); 849 | } 850 | constexpr const_iterator end() const noexcept 851 | { 852 | return data() + size(); 853 | } 854 | 855 | reverse_iterator rbegin() noexcept 856 | { 857 | return reverse_iterator(end()); 858 | } 859 | const_reverse_iterator rbegin() const noexcept 860 | { 861 | return const_reverse_iterator(end()); 862 | } 863 | reverse_iterator rend() noexcept 864 | { 865 | return reverse_iterator(begin()); 866 | } 867 | const_reverse_iterator rend() const noexcept 868 | { 869 | return const_reverse_iterator(begin()); 870 | } 871 | 872 | constexpr const_iterator cbegin() noexcept 873 | { 874 | return begin(); 875 | } 876 | constexpr const_iterator cbegin() const noexcept 877 | { 878 | return begin(); 879 | } 880 | constexpr const_iterator cend() noexcept 881 | { 882 | return end(); 883 | } 884 | constexpr const_iterator cend() const noexcept 885 | { 886 | return end(); 887 | } 888 | 889 | ///@} // Iterators 890 | 891 | private: 892 | /// \name Iterator bound-check utilites 893 | ///@{ 894 | 895 | template 896 | constexpr void assert_iterator_in_range(It it) noexcept 897 | { 898 | static_assert(fcv_detail::Pointer); 899 | FCV_EXPECT(begin() <= it && "iterator not in range"); 900 | FCV_EXPECT(it <= end() && "iterator not in range"); 901 | } 902 | 903 | template 904 | constexpr void assert_valid_iterator_pair(It0 first, 905 | It1 last) noexcept 906 | { 907 | static_assert(fcv_detail::Pointer); 908 | static_assert(fcv_detail::Pointer); 909 | FCV_EXPECT(first <= last && "invalid iterator pair"); 910 | } 911 | 912 | template 913 | constexpr void assert_iterator_pair_in_range(It0 first, 914 | It1 last) noexcept 915 | { 916 | assert_iterator_in_range(first); 917 | assert_iterator_in_range(last); 918 | assert_valid_iterator_pair(first, last); 919 | } 920 | 921 | ///@} 922 | public: 923 | /// \name Element access 924 | /// 925 | ///@{ 926 | 927 | /// Unchecked access to element at index \p pos (UB if index not in 928 | /// range) 929 | constexpr reference operator[](size_type pos) noexcept 930 | { 931 | return fcv_detail::index(*this, pos); 932 | } 933 | 934 | /// Unchecked access to element at index \p pos (UB if index not in 935 | /// range) 936 | constexpr const_reference operator[](size_type pos) const noexcept 937 | { 938 | return fcv_detail::index(*this, pos); 939 | } 940 | 941 | /// Checked access to element at index \p pos (throws `out_of_range` 942 | /// if index not in range) 943 | constexpr reference at(size_type pos) 944 | { 945 | if (FCV_UNLIKELY(pos >= size())) 946 | { 947 | throw out_of_range("fixed_capacity_vector::at"); 948 | } 949 | return fcv_detail::index(*this, pos); 950 | } 951 | 952 | /// Checked access to element at index \p pos (throws `out_of_range` 953 | /// if index not in range) 954 | constexpr const_reference at(size_type pos) const 955 | { 956 | if (FCV_UNLIKELY(pos >= size())) 957 | { 958 | throw out_of_range("fixed_capacity_vector::at"); 959 | } 960 | return fcv_detail::index(*this, pos); 961 | } 962 | 963 | /// 964 | constexpr reference front() noexcept 965 | { 966 | return fcv_detail::index(*this, 0); 967 | } 968 | constexpr const_reference front() const noexcept 969 | { 970 | return fcv_detail::index(*this, 0); 971 | } 972 | 973 | constexpr reference back() noexcept 974 | { 975 | FCV_EXPECT(!empty() && "calling back on an empty vector"); 976 | return fcv_detail::index(*this, size() - 1); 977 | } 978 | constexpr const_reference back() const noexcept 979 | { 980 | FCV_EXPECT(!empty() && "calling back on an empty vector"); 981 | return fcv_detail::index(*this, size() - 1); 982 | } 983 | 984 | ///@} // Element access 985 | 986 | /// \name Modifiers 987 | ///@{ 988 | 989 | using base_t::emplace_back; 990 | using base_t::pop_back; 991 | 992 | /// Clears the vector. 993 | constexpr void clear() noexcept 994 | { 995 | unsafe_destroy_all(); 996 | unsafe_set_size(0); 997 | } 998 | 999 | /// Appends \p value at the end of the vector. 1000 | template && 1002 | fcv_detail::Assignable)> 1003 | constexpr void push_back(U&& value) noexcept( 1004 | noexcept(emplace_back(forward(value)))) 1005 | { 1006 | FCV_EXPECT(!full() && "vector is full!"); 1007 | emplace_back(forward(value)); 1008 | } 1009 | 1010 | /// Appends a default constructed `T` at the end of the vector. 1011 | FCV_REQUIRES(fcv_detail::Constructible&& 1012 | fcv_detail::Assignable) 1013 | void push_back() noexcept(noexcept(emplace_back(T{}))) 1014 | { 1015 | FCV_EXPECT(!full() && "vector is full!"); 1016 | emplace_back(T{}); 1017 | } 1018 | 1019 | template )> 1021 | constexpr iterator 1022 | emplace(const_iterator position, Args&&... args) noexcept( 1023 | noexcept(move_insert(position, declval(), 1024 | declval()))) 1025 | { 1026 | FCV_EXPECT(!full() 1027 | && "tried emplace on full fixed_capacity_vector!"); 1028 | assert_iterator_in_range(position); 1029 | value_type a(forward(args)...); 1030 | return move_insert(position, &a, &a + 1); 1031 | } 1032 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1033 | constexpr iterator insert( 1034 | const_iterator position, 1035 | const_reference x) noexcept(noexcept(insert(position, 1036 | size_type(1), x))) 1037 | { 1038 | FCV_EXPECT(!full() 1039 | && "tried insert on full fixed_capacity_vector!"); 1040 | assert_iterator_in_range(position); 1041 | return insert(position, size_type(1), x); 1042 | } 1043 | 1044 | FCV_REQUIRES(fcv_detail::MoveConstructible) 1045 | constexpr iterator insert( 1046 | const_iterator position, 1047 | value_type&& x) noexcept(noexcept(move_insert(position, &x, 1048 | &x + 1))) 1049 | { 1050 | FCV_EXPECT(!full() 1051 | && "tried insert on full fixed_capacity_vector!"); 1052 | assert_iterator_in_range(position); 1053 | return move_insert(position, &x, &x + 1); 1054 | } 1055 | 1056 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1057 | constexpr iterator insert( 1058 | const_iterator position, size_type n, 1059 | const T& x) noexcept(noexcept(push_back(x))) 1060 | { 1061 | assert_iterator_in_range(position); 1062 | const auto new_size = size() + n; 1063 | FCV_EXPECT(new_size <= capacity() 1064 | && "trying to insert beyond capacity!"); 1065 | auto b = end(); 1066 | while (n != 0) 1067 | { 1068 | push_back(x); 1069 | --n; 1070 | } 1071 | 1072 | auto writable_position = begin() + (position - begin()); 1073 | fcv_detail::slow_rotate(writable_position, b, end()); 1074 | return writable_position; 1075 | } 1076 | 1077 | template and 1080 | fcv_detail::Constructible< 1081 | value_type, 1082 | fcv_detail::iterator_reference_t>)> 1083 | constexpr iterator insert( 1084 | const_iterator position, InputIt first, 1085 | InputIt last) noexcept(noexcept(emplace_back(*first))) 1086 | { 1087 | assert_iterator_in_range(position); 1088 | assert_valid_iterator_pair(first, last); 1089 | if constexpr (fcv_detail::RandomAccessIterator) 1090 | { 1091 | FCV_EXPECT(size() + static_cast(last - first) 1092 | <= capacity() 1093 | && "trying to insert beyond capacity!"); 1094 | } 1095 | auto b = end(); 1096 | 1097 | // insert at the end and then just rotate: 1098 | // cannot use try in constexpr function 1099 | // try { // if copy_constructor throws you get basic-guarantee? 1100 | for (; first != last; ++first) 1101 | { 1102 | emplace_back(*first); 1103 | } 1104 | // } catch (...) { 1105 | // erase(b, end()); 1106 | // throw; 1107 | // } 1108 | 1109 | auto writable_position = begin() + (position - begin()); 1110 | fcv_detail::slow_rotate(writable_position, b, end()); 1111 | return writable_position; 1112 | } 1113 | 1114 | template )> 1116 | constexpr iterator move_insert( 1117 | const_iterator position, InputIt first, 1118 | InputIt last) noexcept(noexcept(emplace_back(move(*first)))) 1119 | { 1120 | assert_iterator_in_range(position); 1121 | assert_valid_iterator_pair(first, last); 1122 | if constexpr (fcv_detail::RandomAccessIterator) 1123 | { 1124 | FCV_EXPECT(size() + static_cast(last - first) 1125 | <= capacity() 1126 | && "trying to insert beyond capacity!"); 1127 | } 1128 | iterator b = end(); 1129 | 1130 | // we insert at the end and then just rotate: 1131 | for (; first != last; ++first) 1132 | { 1133 | emplace_back(move(*first)); 1134 | } 1135 | auto writable_position = begin() + (position - begin()); 1136 | fcv_detail::slow_rotate(writable_position, b, end()); 1137 | return writable_position; 1138 | } 1139 | 1140 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1141 | constexpr iterator insert( 1142 | const_iterator position, 1143 | initializer_list il) noexcept(noexcept(insert(position, 1144 | il.begin(), 1145 | il.end()))) 1146 | { 1147 | assert_iterator_in_range(position); 1148 | return insert(position, il.begin(), il.end()); 1149 | } 1150 | 1151 | FCV_REQUIRES(fcv_detail::Movable) 1152 | constexpr iterator erase(const_iterator position) noexcept 1153 | { 1154 | assert_iterator_in_range(position); 1155 | return erase(position, position + 1); 1156 | } 1157 | 1158 | FCV_REQUIRES(fcv_detail::Movable) 1159 | constexpr iterator erase(const_iterator first, 1160 | const_iterator last) noexcept 1161 | { 1162 | assert_iterator_pair_in_range(first, last); 1163 | iterator p = begin() + (first - begin()); 1164 | if (first != last) 1165 | { 1166 | unsafe_destroy( 1167 | fcv_detail::move(p + (last - first), end(), p), end()); 1168 | unsafe_set_size(size() 1169 | - static_cast(last - first)); 1170 | } 1171 | 1172 | return p; 1173 | } 1174 | 1175 | FCV_REQUIRES(fcv_detail::Assignable) 1176 | constexpr void swap(fixed_capacity_vector& other) noexcept( 1177 | is_nothrow_swappable_v) 1178 | { 1179 | fixed_capacity_vector tmp = move(other); 1180 | other = move(*this); 1181 | (*this) = move(tmp); 1182 | } 1183 | 1184 | /// Resizes the container to contain \p sz elements. If elements 1185 | /// need to be appended, these are copy-constructed from \p value. 1186 | /// 1187 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1188 | constexpr void resize(size_type sz, T const& value) noexcept( 1189 | is_nothrow_copy_constructible_v) 1190 | { 1191 | if (sz == size()) 1192 | { 1193 | return; 1194 | } 1195 | if (sz > size()) 1196 | { 1197 | FCV_EXPECT(sz <= capacity() 1198 | && "fixed_capacity_vector cannot be resized to " 1199 | "a size greater than capacity"); 1200 | insert(end(), sz - size(), value); 1201 | } 1202 | else 1203 | { 1204 | erase(end() - (size() - sz), end()); 1205 | } 1206 | } 1207 | 1208 | private: 1209 | FCV_REQUIRES(fcv_detail::MoveConstructible< 1210 | T> or fcv_detail::CopyConstructible) 1211 | constexpr void emplace_n(size_type n) noexcept( 1212 | (fcv_detail::MoveConstructible< 1213 | T> && is_nothrow_move_constructible_v) 1214 | || (fcv_detail::CopyConstructible< 1215 | T> && is_nothrow_copy_constructible_v)) 1216 | { 1217 | FCV_EXPECT(n <= capacity() 1218 | && "fixed_capacity_vector cannot be " 1219 | "resized to a size greater than " 1220 | "capacity"); 1221 | while (n != size()) 1222 | { 1223 | emplace_back(T{}); 1224 | } 1225 | } 1226 | 1227 | public: 1228 | /// Resizes the container to contain \p sz elements. If elements 1229 | /// need to be appended, these are move-constructed from `T{}` (or 1230 | /// copy-constructed if `T` is not `fcv_detail::MoveConstructible`). 1231 | FCV_REQUIRES(fcv_detail::Movable) 1232 | constexpr void resize(size_type sz) noexcept( 1233 | (fcv_detail::MoveConstructible< 1234 | T> && is_nothrow_move_constructible_v) 1235 | || (fcv_detail::CopyConstructible< 1236 | T> && is_nothrow_copy_constructible_v)) 1237 | { 1238 | if (sz == size()) 1239 | { 1240 | return; 1241 | } 1242 | 1243 | if (sz > size()) 1244 | { 1245 | emplace_n(sz); 1246 | } 1247 | else 1248 | { 1249 | erase(end() - (size() - sz), end()); 1250 | } 1251 | } 1252 | 1253 | ///@} // Modifiers 1254 | 1255 | /// \name Construct/copy/move/destroy 1256 | ///@{ 1257 | 1258 | /// Default constructor. 1259 | constexpr fixed_capacity_vector() = default; 1260 | 1261 | /// Copy constructor. 1262 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1263 | constexpr fixed_capacity_vector( 1264 | fixed_capacity_vector const& 1265 | other) noexcept(noexcept(insert(begin(), other.begin(), 1266 | other.end()))) 1267 | { 1268 | // nothin to assert: size of other cannot exceed capacity 1269 | // because both vectors have the same type 1270 | insert(begin(), other.begin(), other.end()); 1271 | } 1272 | 1273 | /// Move constructor. 1274 | FCV_REQUIRES(fcv_detail::MoveConstructible) 1275 | constexpr fixed_capacity_vector( 1276 | fixed_capacity_vector&& 1277 | other) noexcept(noexcept(move_insert(begin(), other.begin(), 1278 | other.end()))) 1279 | { 1280 | // nothin to assert: size of other cannot exceed capacity 1281 | // because both vectors have the same type 1282 | move_insert(begin(), other.begin(), other.end()); 1283 | } 1284 | 1285 | /// Copy assignment. 1286 | FCV_REQUIRES(fcv_detail::Assignable) 1287 | constexpr fixed_capacity_vector& 1288 | operator=(fixed_capacity_vector const& other) noexcept( 1289 | noexcept(clear()) 1290 | && noexcept(insert(begin(), other.begin(), other.end()))) 1291 | { 1292 | // nothin to assert: size of other cannot exceed capacity 1293 | // because both vectors have the same type 1294 | clear(); 1295 | insert(this->begin(), other.begin(), other.end()); 1296 | return *this; 1297 | } 1298 | 1299 | /// Move assignment. 1300 | FCV_REQUIRES(fcv_detail::Assignable) 1301 | constexpr fixed_capacity_vector& 1302 | operator=(fixed_capacity_vector&& other) noexcept( 1303 | noexcept(clear()) 1304 | and noexcept(move_insert(begin(), other.begin(), other.end()))) 1305 | { 1306 | // nothin to assert: size of other cannot exceed capacity 1307 | // because both vectors have the same type 1308 | clear(); 1309 | move_insert(this->begin(), other.begin(), other.end()); 1310 | return *this; 1311 | } 1312 | 1313 | /// Initializes vector with \p n default-constructed elements. 1314 | FCV_REQUIRES(fcv_detail::CopyConstructible< 1315 | T> or fcv_detail::MoveConstructible) 1316 | explicit constexpr fixed_capacity_vector(size_type n) noexcept( 1317 | noexcept(emplace_n(n))) 1318 | { 1319 | FCV_EXPECT(n <= capacity() && "size exceeds capacity"); 1320 | emplace_n(n); 1321 | } 1322 | 1323 | /// Initializes vector with \p n with \p value. 1324 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1325 | constexpr fixed_capacity_vector( 1326 | size_type n, 1327 | T const& value) noexcept(noexcept(insert(begin(), n, value))) 1328 | { 1329 | FCV_EXPECT(n <= capacity() && "size exceeds capacity"); 1330 | insert(begin(), n, value); 1331 | } 1332 | 1333 | /// Initialize vector from range [first, last). 1334 | template )> 1336 | constexpr fixed_capacity_vector(InputIt first, InputIt last) 1337 | { 1338 | if constexpr (fcv_detail::RandomAccessIterator) 1339 | { 1340 | FCV_EXPECT(last - first >= 0); 1341 | FCV_EXPECT(static_cast(last - first) 1342 | <= capacity() 1343 | && "range size exceeds capacity"); 1344 | } 1345 | insert(begin(), first, last); 1346 | } 1347 | 1348 | template )> 1350 | constexpr fixed_capacity_vector(initializer_list il) noexcept( 1351 | noexcept(base_t(move(il)))) 1352 | : base_t(move(il)) 1353 | { // assert happens in base_t constructor 1354 | } 1355 | 1356 | template )> 1358 | constexpr void assign(InputIt first, InputIt last) noexcept( 1359 | noexcept(clear()) and noexcept(insert(begin(), first, last))) 1360 | { 1361 | if constexpr (fcv_detail::RandomAccessIterator) 1362 | { 1363 | FCV_EXPECT(last - first >= 0); 1364 | FCV_EXPECT(static_cast(last - first) 1365 | <= capacity() 1366 | && "range size exceeds capacity"); 1367 | } 1368 | clear(); 1369 | insert(begin(), first, last); 1370 | } 1371 | 1372 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1373 | constexpr void assign(size_type n, const T& u) 1374 | { 1375 | FCV_EXPECT(n <= capacity() && "size exceeds capacity"); 1376 | clear(); 1377 | insert(begin(), n, u); 1378 | } 1379 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1380 | constexpr void assign(initializer_list const& il) 1381 | { 1382 | FCV_EXPECT(il.size() <= capacity() 1383 | && "initializer_list size exceeds capacity"); 1384 | clear(); 1385 | insert(this->begin(), il.begin(), il.end()); 1386 | } 1387 | FCV_REQUIRES(fcv_detail::CopyConstructible) 1388 | constexpr void assign(initializer_list&& il) 1389 | { 1390 | FCV_EXPECT(il.size() <= capacity() 1391 | && "initializer_list size exceeds capacity"); 1392 | clear(); 1393 | insert(this->begin(), il.begin(), il.end()); 1394 | } 1395 | 1396 | ///@} // Construct/copy/move/destroy/assign 1397 | }; 1398 | 1399 | template 1400 | constexpr bool operator==( 1401 | fixed_capacity_vector const& a, 1402 | fixed_capacity_vector const& b) noexcept 1403 | { 1404 | return a.size() == b.size() 1405 | and fcv_detail::cmp(a.begin(), a.end(), b.begin(), b.end(), 1406 | equal_to<>{}); 1407 | } 1408 | 1409 | template 1410 | constexpr bool operator<( 1411 | fixed_capacity_vector const& a, 1412 | fixed_capacity_vector const& b) noexcept 1413 | { 1414 | return fcv_detail::cmp(a.begin(), a.end(), b.begin(), b.end(), 1415 | less<>{}); 1416 | } 1417 | 1418 | template 1419 | constexpr bool operator!=( 1420 | fixed_capacity_vector const& a, 1421 | fixed_capacity_vector const& b) noexcept 1422 | { 1423 | return not(a == b); 1424 | } 1425 | 1426 | template 1427 | constexpr bool operator<=( 1428 | fixed_capacity_vector const& a, 1429 | fixed_capacity_vector const& b) noexcept 1430 | { 1431 | return fcv_detail::cmp(a.begin(), a.end(), b.begin(), b.end(), 1432 | less_equal<>{}); 1433 | } 1434 | 1435 | template 1436 | constexpr bool operator>( 1437 | fixed_capacity_vector const& a, 1438 | fixed_capacity_vector const& b) noexcept 1439 | { 1440 | return fcv_detail::cmp(a.begin(), a.end(), b.begin(), b.end(), 1441 | greater<>{}); 1442 | } 1443 | 1444 | template 1445 | constexpr bool operator>=( 1446 | fixed_capacity_vector const& a, 1447 | fixed_capacity_vector const& b) noexcept 1448 | { 1449 | return fcv_detail::cmp(a.begin(), a.end(), b.begin(), b.end(), 1450 | greater_equal<>{}); 1451 | } 1452 | 1453 | 1454 | 1455 | } // namespace experimental 1456 | } // namespace std 1457 | 1458 | // undefine all the internal macros 1459 | #undef FCV_UNREACHABLE 1460 | #undef FCV_ASSUME 1461 | #undef FCV_ASSERT 1462 | #undef FCV_LIKELY 1463 | #undef FCV_UNLIKELY 1464 | #undef FCV_EXPECT 1465 | #undef FCV_CONCEPT_PP_CAT_ 1466 | #undef FCV_CONCEPT_PP_CAT 1467 | #undef FCV_REQUIRES_ 1468 | #undef FCV_REQUIRES 1469 | 1470 | #endif // STD_EXPERIMENTAL_FIXED_CAPACITY_VECTOR 1471 | --------------------------------------------------------------------------------