/
redgpu
/
raygpu
Обзор
Документация
Войти
/
redgpu
/
raygpu
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
include/external/small_vector.hpp
6 201 строка
196 KB
manuel5975p
Installation init
21 фев 2025, 16:25
21 фев 2025, 16:25
2e7ddb5
Код
Авторство
О чём код?
/** small_vector.hpp * An implementation of `small_vector` (a vector with a small * buffer optimization). I would probably have preferred to * call this `inline_vector`, but I'll just go with the canonical * name for now. * * Copyright © 2020-2021 Gene Harvey * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef GCH_SMALL_VECTOR_HPP #define GCH_SMALL_VECTOR_HPP #ifdef __clang__ # ifndef GCH_CLANG # define GCH_CLANG # endif # if defined (__cplusplus) && __cplusplus >= 201103L # ifndef GCH_CLANG_11 # define GCH_CLANG_11 # endif # endif # if defined (__cplusplus) && __cplusplus >= 201402L # ifndef GCH_CLANG_14 # define GCH_CLANG_14 # endif # endif # if defined (__cplusplus) && __cplusplus >= 201703L # ifndef GCH_CLANG_17 # define GCH_CLANG_17 # endif # endif # if defined (__cplusplus) && __cplusplus >= 202002L # ifndef GCH_CLANG_20 # define GCH_CLANG_20 # endif # endif #endif #ifndef GCH_CPP14_CONSTEXPR # if defined (__cpp_constexpr) && __cpp_constexpr >= 201304L # define GCH_CPP14_CONSTEXPR constexpr # ifndef GCH_HAS_CPP14_CONSTEXPR # define GCH_HAS_CPP14_CONSTEXPR # endif # else # define GCH_CPP14_CONSTEXPR # endif #endif #ifndef GCH_CPP17_CONSTEXPR # if defined (__cpp_constexpr) && __cpp_constexpr >= 201603L # define GCH_CPP17_CONSTEXPR constexpr # ifndef GCH_HAS_CPP17_CONSTEXPR # define GCH_HAS_CPP17_CONSTEXPR # endif # else # define GCH_CPP17_CONSTEXPR # endif #endif #ifndef GCH_CPP20_CONSTEXPR # if defined (__cpp_constexpr) && __cpp_constexpr >= 201907L # define GCH_CPP20_CONSTEXPR constexpr # ifndef GCH_HAS_CPP20_CONSTEXPR # define GCH_HAS_CPP20_CONSTEXPR # endif # else # define GCH_CPP20_CONSTEXPR # endif #endif #ifndef GCH_NORETURN # if defined (__has_cpp_attribute) && __has_cpp_attribute (noreturn) >= 200809L # define GCH_NORETURN [[noreturn]] # else # define GCH_NORETURN # endif #endif #ifndef GCH_NODISCARD # if defined (__has_cpp_attribute) && __has_cpp_attribute (nodiscard) >= 201603L # if ! defined (__clang__) || defined (GCH_CLANG_17) # define GCH_NODISCARD [[nodiscard]] # else # define GCH_NODISCARD # endif # else # define GCH_NODISCARD # endif #endif #ifndef GCH_INLINE_VARIABLE # if defined (__cpp_inline_variables) && __cpp_inline_variables >= 201606L # define GCH_INLINE_VARIABLE inline # else # define GCH_INLINE_VARIABLE # endif #endif #ifndef GCH_EMPTY_BASE # if defined (_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918L # define GCH_EMPTY_BASE __declspec (empty_bases) # else # define GCH_EMPTY_BASE # endif #endif #ifndef GCH_IMPLICIT_CONVERSION # if defined (__cpp_conditional_explicit) && __cpp_conditional_explicit >= 201806L # define GCH_IMPLICIT_CONVERSION explicit (false) # else # define GCH_IMPLICIT_CONVERSION /* implicit */ # endif #endif #if defined (__cpp_variable_templates) && __cpp_variable_templates >= 201304L # ifndef GCH_VARIABLE_TEMPLATES # define GCH_VARIABLE_TEMPLATES # endif #endif #if defined (__cpp_deduction_guides) && __cpp_deduction_guides >= 201703L # ifndef GCH_CTAD_SUPPORT # define GCH_CTAD_SUPPORT # endif #endif #ifndef GCH_IF_CONSTEXPR # if defined (__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L # define GCH_IF_CONSTEXPR constexpr # ifndef GCH_HAS_IF_CONSTEXPR # define GCH_HAS_IF_CONSTEXPR # endif # else # define GCH_IF_CONSTEXPR # endif #endif #if defined (__cpp_exceptions) && __cpp_exceptions >= 199711L # ifndef GCH_EXCEPTIONS # define GCH_EXCEPTIONS # endif #endif #ifndef GCH_TRY # ifdef GCH_EXCEPTIONS # define GCH_TRY try # else # define GCH_TRY if GCH_IF_CONSTEXPR (true) # endif #endif #ifndef GCH_CATCH # ifdef GCH_EXCEPTIONS # define GCH_CATCH(...) catch (__VA_ARGS__) # else # define GCH_CATCH(...) else if GCH_IF_CONSTEXPR (false) # endif #endif #ifndef GCH_THROW # ifdef GCH_EXCEPTIONS # define GCH_THROW throw # else # define GCH_THROW # endif #endif #ifndef GCH_CONSTEVAL # if defined (__cpp_consteval) && __cpp_consteval >= 201811L # define GCH_CONSTEVAL consteval # ifndef GCH_HAS_CONSTEVAL # define GCH_HAS_CONSTEVAL # endif # else # define GCH_CONSTEVAL constexpr # endif #endif #if defined (__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L # ifndef GCH_IMPL_THREE_WAY_COMPARISON # define GCH_IMPL_THREE_WAY_COMPARISON # endif #endif #if defined (__cpp_concepts) && __cpp_concepts >= 201907L # ifndef GCH_CONCEPTS # define GCH_CONCEPTS # endif #endif #include <algorithm> #include <cassert> #include <cstddef> #include <cstdint> #include <cstring> #include <initializer_list> #include <iterator> #include <limits> #include <memory> #include <new> #include <type_traits> #include <utility> #ifdef GCH_IMPL_THREE_WAY_COMPARISON # if defined (__has_include) && __has_include (<compare>) # include <compare> # endif #endif #ifdef GCH_CONCEPTS # if defined (__has_include) && __has_include (<concepts>) # include <concepts> # endif #endif #ifdef GCH_STDLIB_INTEROP # include <array> # include <valarray> # include <vector> #endif #ifdef GCH_EXCEPTIONS # include <stdexcept> #else # include <cstdio> # include <cstdlib> #endif #if defined (__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L # ifndef GCH_LIB_THREE_WAY_COMPARISON # define GCH_LIB_THREE_WAY_COMPARISON # endif #endif #if defined (__cpp_lib_concepts) && __cpp_lib_concepts >= 202002L # if ! defined (GCH_LIB_CONCEPTS) && ! defined (GCH_DISABLE_CONCEPTS) # define GCH_LIB_CONCEPTS # endif #endif #if defined (__cpp_lib_is_final) && __cpp_lib_is_final >= 201402L # ifndef GCH_LIB_IS_FINAL # define GCH_LIB_IS_FINAL # endif #endif #if defined (__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L # ifndef GCH_LIB_IS_CONSTANT_EVALUATED # define GCH_LIB_IS_CONSTANT_EVALUATED # endif #endif #if defined (__cpp_lib_is_swappable) && __cpp_lib_is_swappable >= 201603L # ifndef GCH_LIB_IS_SWAPPABLE # define GCH_LIB_IS_SWAPPABLE # endif #endif #if defined (__cpp_lib_allocator_traits_is_always_equal) # if __cpp_lib_allocator_traits_is_always_equal >= 201411L # ifndef GCH_LIB_IS_ALWAYS_EQUAL # define GCH_LIB_IS_ALWAYS_EQUAL # endif # endif #endif #if defined (__cpp_lib_constexpr_memory) && __cpp_lib_constexpr_memory >= 201811L # ifndef GCH_LIB_CONSTEXPR_MEMORY # define GCH_LIB_CONSTEXPR_MEMORY # endif #endif // TODO: // Make sure we don't need any laundering in the internal class functions. // I also need some sort of test case to actually show where UB is occurring, // because it's still a bit unclear to me. #if defined (__cpp_lib_launder) && __cpp_lib_launder >= 201606L # ifndef GCH_LIB_LAUNDER # define GCH_LIB_LAUNDER # endif #endif // defined if the entire thing is available for constexpr #ifndef GCH_SMALL_VECTOR_CONSTEXPR # if defined (GCH_HAS_CPP20_CONSTEXPR) && defined (GCH_LIB_IS_CONSTANT_EVALUATED) \ && defined (GCH_LIB_CONSTEXPR_MEMORY) # define GCH_SMALL_VECTOR_CONSTEXPR constexpr # ifndef GCH_HAS_CONSTEXPR_SMALL_VECTOR # define GCH_HAS_CONSTEXPR_SMALL_VECTOR # endif # else # define GCH_SMALL_VECTOR_CONSTEXPR # endif #endif #ifndef GCH_SMALL_VECTOR_DEFAULT_SIZE # define GCH_SMALL_VECTOR_DEFAULT_SIZE 64 #endif namespace gch { #ifdef GCH_LIB_CONCEPTS namespace concepts { template <typename T> concept Complete = requires { sizeof (T); }; // Note: this mirrors the named requirements, not the standard concepts, so we don't require // the destructor to be noexcept for Destructible. template <typename T> concept Destructible = std::is_destructible<T>::value; template <typename T> concept TriviallyDestructible = std::is_trivially_destructible<T>::value; template <typename T> concept NoThrowDestructible = std::is_nothrow_destructible<T>::value; // Note: this mirrors the named requirements, not the standard library concepts, // so we don't require Destructible here. template <typename T, typename... Args> concept ConstructibleFrom = std::is_constructible<T, Args...>::value; template <typename T, typename... Args> concept NoThrowConstructibleFrom = std::is_nothrow_constructible<T, Args...>::value; template <typename From, typename To> concept ConvertibleTo = std::is_convertible<From, To>::value && requires (typename std::add_rvalue_reference<From>::type (&f) (void)) { static_cast<To> (f ()); }; template <typename From, typename To> concept NoThrowConvertibleTo = std::is_nothrow_convertible<From, To>::value && requires (typename std::add_rvalue_reference<From>::type (&f) (void) noexcept) { { static_cast<To> (f ()) } noexcept; }; // Note: std::default_initializable requires std::destructible. template <typename T> concept DefaultConstructible = ConstructibleFrom<T> && requires { T { }; } && requires { ::new (static_cast<void *> (nullptr)) T; }; template <typename T> concept MoveAssignable = std::assignable_from<T&, T>; template <typename T> concept CopyAssignable = MoveAssignable<T> && std::assignable_from<T&, T&> && std::assignable_from<T&, const T&> && std::assignable_from<T&, const T>; template <typename T> concept MoveConstructible = ConstructibleFrom<T, T> && ConvertibleTo<T, T>; template <typename T> concept NoThrowMoveConstructible = NoThrowConstructibleFrom<T, T> && NoThrowConvertibleTo<T, T>; template <typename T> concept CopyConstructible = MoveConstructible<T> && ConstructibleFrom<T, T&> && ConvertibleTo< T&, T> && ConstructibleFrom<T, const T&> && ConvertibleTo<const T&, T> && ConstructibleFrom<T, const T > && ConvertibleTo<const T , T>; template <typename T> concept NoThrowCopyConstructible = NoThrowMoveConstructible<T> && NoThrowConstructibleFrom<T, T&> && NoThrowConvertibleTo< T&, T> && NoThrowConstructibleFrom<T, const T&> && NoThrowConvertibleTo<const T&, T> && NoThrowConstructibleFrom<T, const T > && NoThrowConvertibleTo<const T , T>; template <typename T> concept Swappable = std::swappable<T>; template <typename T> concept EqualityComparable = std::equality_comparable<T>; // T is a type // X is a Container // A is an Allocator // if X::allocator_type then // std::same_as<typename X::allocator_type, // typename std::allocator_traits<A>::template rebind_alloc<T>> // otherwise // no condition; we use std::allocator<T> regardless of A // // see [22.2.1].16 template <typename T, typename X, typename A, typename ...Args> concept EmplaceConstructible = std::same_as<typename X::value_type, T> && ( ( requires { typename X::allocator_type; } // only perform this check if X is && std::same_as<typename X::allocator_type, // allocator-aware typename std::allocator_traits<A>::template rebind_alloc<T>> && ( requires (A m, T *p, Args&&... args) { m.construct (p, std::forward<Args> (args)...); } || requires (T *p, Args&&... args) { #if __cplusplus >= 202002L // C++20 fully featured { std::construct_at (p, std::forward<Args> (args)...) } -> std::same_as<T *>; #else ::new (std::declval<void *> ()) T (std::declval<Args> ()...); #endif })) || (! requires { typename X::allocator_type; } && requires (T *p, Args&&... args) { #if __cplusplus >= 202002L // C++20 fully featured { std::construct_at (p, std::forward<Args> (args)...) } -> std::same_as<T *>; #else ::new (std::declval<void *> ()) T (std::declval<Args> ()...); #endif })); template <typename T, typename X, typename A = typename std::conditional<requires { typename X::allocator_type; }, typename X::allocator_type, std::allocator<T>>::type> concept DefaultInsertable = EmplaceConstructible<T, X, A>; template <typename T, typename X, typename A = typename std::conditional<requires { typename X::allocator_type; }, typename X::allocator_type, std::allocator<T>>::type> concept MoveInsertable = EmplaceConstructible<T, X, A, T>; template <typename T, typename X, typename A = typename std::conditional<requires { typename X::allocator_type; }, typename X::allocator_type, std::allocator<T>>::type> concept CopyInsertable = MoveInsertable<T, X, A> && EmplaceConstructible<T, X, A, T&> && EmplaceConstructible<T, X, A, const T&>; // same method as with EmplaceConstructible template <typename T, typename X, typename A = typename std::conditional<requires { typename X::allocator_type; }, typename X::allocator_type, std::allocator<T>>::type> concept Erasable = std::same_as<typename X::value_type, T> && ( ( requires { typename X::allocator_type; } // if X is allocator aware && std::same_as<typename X::allocator_type, typename std::allocator_traits<A>::template rebind_alloc<T>> && ( requires (A m, T *p) { m.destroy (p); } || std::is_destructible<T>::value)) || (! requires { typename X::allocator_type; } && std::is_destructible<T>::value)); template <typename T> concept ContextuallyConvertibleToBool = std::constructible_from<bool, T>; template <typename T> concept BoolConstant = std::derived_from<T, std::true_type> || std::derived_from<T, std::false_type>; template <typename T> concept NullablePointer = EqualityComparable<T> && DefaultConstructible<T> && CopyConstructible<T> && CopyAssignable<T> && Destructible<T> && ConstructibleFrom<T, std::nullptr_t> && ConvertibleTo<std::nullptr_t, T> && requires (T p, T q, std::nullptr_t np) { T (np); { p = np } -> std::same_as<T&>; { p != q } -> ContextuallyConvertibleToBool; { p == np } -> ContextuallyConvertibleToBool; { np == p } -> ContextuallyConvertibleToBool; { p != np } -> ContextuallyConvertibleToBool; { np != p } -> ContextuallyConvertibleToBool; }; static_assert ( NullablePointer<int *>); static_assert (! NullablePointer<int>); template <typename A, typename T, typename U = T *> concept AllocatorFor = NoThrowCopyConstructible<A> && requires (A a, typename std::allocator_traits<A>::template rebind_alloc<U> b, U xp, typename std::allocator_traits<A>::pointer p, typename std::allocator_traits<A>::const_pointer cp, typename std::allocator_traits<A>::void_pointer vp, typename std::allocator_traits<A>::const_void_pointer cvp, typename std::allocator_traits<A>::value_type& r, typename std::allocator_traits<A>::size_type n) { /** Inner types **/ // A::pointer requires NullablePointer< typename std::allocator_traits<A>::pointer>; requires std::random_access_iterator<typename std::allocator_traits<A>::pointer>; requires std::contiguous_iterator< typename std::allocator_traits<A>::pointer>; // A::const_pointer requires NullablePointer< typename std::allocator_traits<A>::const_pointer>; requires std::random_access_iterator<typename std::allocator_traits<A>::const_pointer>; requires std::contiguous_iterator< typename std::allocator_traits<A>::const_pointer>; requires std::convertible_to<typename std::allocator_traits<A>::pointer, typename std::allocator_traits<A>::const_pointer>; // A::void_pointer requires NullablePointer<typename std::allocator_traits<A>::void_pointer>; requires std::convertible_to<typename std::allocator_traits<A>::pointer, typename std::allocator_traits<A>::void_pointer>; requires std::same_as<typename std::allocator_traits<A>::void_pointer, typename std::allocator_traits<decltype (b)>::void_pointer>; // A::const_void_pointer requires NullablePointer<typename std::allocator_traits<A>::const_void_pointer>; requires std::convertible_to<typename std::allocator_traits<A>::pointer, typename std::allocator_traits<A>::const_void_pointer>; requires std::convertible_to<typename std::allocator_traits<A>::const_pointer, typename std::allocator_traits<A>::const_void_pointer>; requires std::convertible_to<typename std::allocator_traits<A>::void_pointer, typename std::allocator_traits<A>::const_void_pointer>; requires std::same_as<typename std::allocator_traits<A>::const_void_pointer, typename std::allocator_traits<decltype (b)>::const_void_pointer>; // A::value_type typename A::value_type; requires std::same_as<typename A::value_type, T>; requires std::same_as<typename A::value_type, typename std::allocator_traits<A>::value_type>; // A::size_type requires std::unsigned_integral<typename std::allocator_traits<A>::size_type>; // A::difference_type requires std::signed_integral<typename std::allocator_traits<A>::difference_type>; // A::template rebind<U>::other [optional] requires ! requires { typename A::template rebind<U>::other; } || requires { requires std::same_as<decltype (b), typename A::template rebind<U>::other>; requires std::same_as<A, typename decltype (b)::template rebind<T>::other>; }; /** Operations on pointers **/ { *p } -> std::same_as<typename A::value_type&>; { *cp } -> std::same_as<const typename A::value_type&>; // Language in the standard implies that `decltype (p)` must either // be a raw pointer or implement `operator->`. There is no mention // of `std::to_address` or `std::pointer_traits<Ptr>::to_address`. requires std::same_as<decltype (p), typename A::value_type *> || requires { { p.operator-> () } -> std::same_as<typename A::value_type *>; }; requires std::same_as<decltype (cp), const typename A::value_type *> || requires { { cp.operator-> () } -> std::same_as<const typename A::value_type *>; }; { static_cast<decltype (p)> (vp) } -> std::same_as<decltype (p)>; { static_cast<decltype (cp)> (cvp) } -> std::same_as<decltype (cp)>; { std::pointer_traits<decltype (p)>::pointer_to (r) } -> std::same_as<decltype (p)>; /** Storage and lifetime operations **/ // a.allocate (n) { a.allocate (n) } -> std::same_as<decltype (p)>; // a.allocate (n, cvp) [optional] requires ! requires { a.allocate (n, cvp); } || requires { { a.allocate (n, cvp) } -> std::same_as<decltype (p)>; }; // a.deallocate (p, n) { a.deallocate (p, n) } -> std::convertible_to<void>; // a.max_size () [optional] requires ! requires { a.max_size (); } || requires { { a.max_size () } -> std::same_as<decltype (n)>; }; // a.construct (xp, args) [optional] requires ! requires { a.construct (xp); } || requires { { a.construct (xp) } -> std::convertible_to<void>; }; // a.destroy (xp) [optional] requires ! requires { a.destroy (xp); } || requires { { a.destroy (xp) } -> std::convertible_to<void>; }; /** Relationship between instances **/ requires NoThrowConstructibleFrom<A, decltype (b)>; requires NoThrowConstructibleFrom<A, decltype (std::move (b))>; requires BoolConstant<typename std::allocator_traits<A>::is_always_equal>; /** Influence on container operations **/ // a.select_on_container_copy_construction () [optional] requires ! requires { a.select_on_container_copy_construction (); } || requires { { a.select_on_container_copy_construction () } -> std::same_as<A>; }; requires BoolConstant< typename std::allocator_traits<A>::propagate_on_container_copy_assignment>; requires BoolConstant< typename std::allocator_traits<A>::propagate_on_container_move_assignment>; requires BoolConstant< typename std::allocator_traits<A>::propagate_on_container_swap>; { a == b } -> std::same_as<bool>; { a != b } -> std::same_as<bool>; } && requires (A a1, A a2) { { a1 == a2 } -> std::same_as<bool>; { a1 != a2 } -> std::same_as<bool>; }; static_assert (AllocatorFor<std::allocator<int>, int>, "std::allocator<int> failed to meet Allocator concept requirements."); template <typename A> concept Allocator = AllocatorFor<A, typename A::value_type>; namespace small_vector { // Basically, these shut off the concepts if we have an incomplete type. // This namespace is only needed because of issues on Clang // preventing us from short-circuiting for incomplete types. template <typename T> concept Destructible = ! concepts::Complete<T> || concepts::Destructible<T>; template <typename T> concept MoveAssignable = ! concepts::Complete<T> || concepts::MoveAssignable<T>; template <typename T> concept CopyAssignable = ! concepts::Complete<T> || concepts::CopyAssignable<T>; template <typename T> concept MoveConstructible = ! concepts::Complete<T> || concepts::MoveConstructible<T>; template <typename T> concept CopyConstructible = ! concepts::Complete<T> || concepts::CopyConstructible<T>; template <typename T> concept Swappable = ! concepts::Complete<T> || concepts::Swappable<T>; template <typename T, typename SmallVector, typename Alloc> concept DefaultInsertable = ! concepts::Complete<T> || concepts::DefaultInsertable<T, SmallVector, Alloc>; template <typename T, typename SmallVector, typename Alloc> concept MoveInsertable = ! concepts::Complete<T> || concepts::MoveInsertable<T, SmallVector, Alloc>; template <typename T, typename SmallVector, typename Alloc> concept CopyInsertable = ! concepts::Complete<T> || concepts::CopyInsertable<T, SmallVector, Alloc>; template <typename T, typename SmallVector, typename Alloc> concept Erasable = ! concepts::Complete<T> || concepts::Erasable<T, SmallVector, Alloc>; template <typename T, typename SmallVector, typename Alloc, typename ...Args> concept EmplaceConstructible = ! concepts::Complete<T> || concepts::EmplaceConstructible<T, SmallVector, Alloc, Args...>; template <typename Alloc, typename T> concept AllocatorFor = ! concepts::Complete<T> || concepts::AllocatorFor<Alloc, T>; template <typename Alloc> concept Allocator = AllocatorFor<Alloc, typename Alloc::value_type>; } // namespace gch::concepts::small_vector } // namespace gch::concepts #endif template <typename Allocator> #ifdef GCH_LIB_CONCEPTS requires concepts::small_vector::Allocator<Allocator> #endif struct default_buffer_size; template <typename T, unsigned InlineCapacity = default_buffer_size<std::allocator<T>>::value, typename Allocator = std::allocator<T>> #ifdef GCH_LIB_CONCEPTS requires concepts::small_vector::AllocatorFor<Allocator, T> #endif class small_vector; template <typename Allocator> #ifdef GCH_LIB_CONCEPTS requires concepts::small_vector::Allocator<Allocator> #endif struct default_buffer_size { private: template <typename, typename Enable = void> struct is_complete : std::false_type { }; template <typename U> struct is_complete<U, decltype (static_cast<void> (sizeof (U)))> : std::true_type { }; public: using allocator_type = Allocator; using value_type = typename std::allocator_traits<allocator_type>::value_type; using empty_small_vector = small_vector<value_type, 0, allocator_type>; static_assert (is_complete<value_type>::value, "Calculation of a default number of elements requires that `T` be complete."); static constexpr unsigned buffer_max = 256; static constexpr unsigned ideal_total = GCH_SMALL_VECTOR_DEFAULT_SIZE; #ifndef GCH_UNRESTRICTED_DEFAULT_BUFFER_SIZE // FIXME: Some compilers will not emit the error from this static_assert // while instantiating a small_vector, and attribute the mistake // to some random other function. // static_assert (sizeof (value_type) <= buffer_max, "`sizeof (T)` too large"); #endif static constexpr unsigned ideal_buffer = ideal_total - sizeof (empty_small_vector); static_assert (sizeof (empty_small_vector) != 0, "Empty `small_vector` should not have size 0."); static_assert (ideal_buffer < ideal_total, "Empty `small_vector` is larger than ideal_total."); static constexpr unsigned value = (sizeof (value_type) <= ideal_buffer) ? (ideal_buffer / sizeof (value_type)) : 1; }; #ifdef GCH_VARIABLE_TEMPLATES template <typename Allocator> GCH_INLINE_VARIABLE constexpr unsigned default_buffer_size_v = default_buffer_size<Allocator>::value; #endif template <typename Pointer, typename DifferenceType> class small_vector_iterator { public: using difference_type = DifferenceType; using value_type = typename std::iterator_traits<Pointer>::value_type; using pointer = typename std::iterator_traits<Pointer>::pointer; using reference = typename std::iterator_traits<Pointer>::reference; using iterator_category = typename std::iterator_traits<Pointer>::iterator_category; #ifdef GCH_LIB_CONCEPTS using iterator_concept = std::contiguous_iterator_tag; #endif // small_vector_iterator (void) = impl; small_vector_iterator (const small_vector_iterator&) = default; small_vector_iterator (small_vector_iterator&&) noexcept = default; small_vector_iterator& operator= (const small_vector_iterator&) = default; small_vector_iterator& operator= (small_vector_iterator&&) noexcept = default; ~small_vector_iterator (void) = default; #ifdef NDEBUG small_vector_iterator (void) = default; #else constexpr small_vector_iterator (void) noexcept : m_ptr () { } #endif constexpr explicit small_vector_iterator (const Pointer& p) noexcept : m_ptr (p) { } template <typename U, typename D, typename std::enable_if<std::is_convertible<U, Pointer>::value>::type * = nullptr> constexpr GCH_IMPLICIT_CONVERSION small_vector_iterator (const small_vector_iterator<U, D>& other) noexcept : m_ptr (other.base ()) { } GCH_CPP14_CONSTEXPR small_vector_iterator& operator++ (void) noexcept { ++m_ptr; return *this; } GCH_CPP14_CONSTEXPR small_vector_iterator operator++ (int) noexcept { return small_vector_iterator (m_ptr++); } GCH_CPP14_CONSTEXPR small_vector_iterator& operator-- (void) noexcept { --m_ptr; return *this; } GCH_CPP14_CONSTEXPR small_vector_iterator operator-- (int) noexcept { return small_vector_iterator (m_ptr--); } GCH_CPP14_CONSTEXPR small_vector_iterator& operator+= (difference_type n) noexcept { m_ptr += n; return *this; } constexpr small_vector_iterator operator+ (difference_type n) const noexcept { return small_vector_iterator (m_ptr + n); } GCH_CPP14_CONSTEXPR small_vector_iterator& operator-= (difference_type n) noexcept { m_ptr -= n; return *this; } constexpr small_vector_iterator operator- (difference_type n) const noexcept { return small_vector_iterator (m_ptr - n); } constexpr reference operator* (void) const noexcept { #ifdef GCH_LIB_LAUNDER return launder_and_dereference (m_ptr); #else return *m_ptr; #endif } constexpr pointer operator-> (void) const noexcept { return get_pointer (m_ptr); } constexpr reference operator[] (difference_type n) const noexcept { #ifdef GCH_LIB_LAUNDER return launder_and_dereference (m_ptr + n); #else return m_ptr[n]; #endif } constexpr const Pointer& base (void) const noexcept { return m_ptr; } private: template <typename Ptr = Pointer, typename std::enable_if<std::is_pointer<Ptr>::value, bool>::type = true> static constexpr pointer get_pointer (Pointer ptr) noexcept { return ptr; } template <typename Ptr = Pointer, typename std::enable_if<! std::is_pointer<Ptr>::value, bool>::type = false> static constexpr pointer get_pointer (Pointer ptr) noexcept { // Given the requirements for Allocator, Pointer must either be a raw pointer, or // have a defined operator-> which returns a raw pointer. return ptr.operator-> (); } #ifdef GCH_LIB_LAUNDER template <typename Ptr = Pointer, typename std::enable_if<std::is_pointer<Ptr>::value, bool>::type = true> static constexpr reference launder_and_dereference (Pointer ptr) noexcept { return *std::launder (ptr); } template <typename Ptr = Pointer, typename std::enable_if<! std::is_pointer<Ptr>::value, bool>::type = false> static constexpr reference launder_and_dereference (Pointer ptr) noexcept { return *ptr; } #endif Pointer m_ptr; }; #ifdef GCH_LIB_THREE_WAY_COMPARISON template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> constexpr bool operator== (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept (noexcept (lhs.base () == rhs.base ())) requires requires { { lhs.base () == rhs.base () } -> std::convertible_to<bool>; } { return lhs.base () == rhs.base (); } template <typename Pointer, typename DifferenceType> constexpr bool operator== (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept (noexcept (lhs.base () == rhs.base ())) requires requires { { lhs.base () == rhs.base () } -> std::convertible_to<bool>; } { return lhs.base () == rhs.base (); } template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> requires std::three_way_comparable_with<PointerLHS, PointerRHS> constexpr auto operator<=> (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept (noexcept (lhs.base () <=> rhs.base ())) { return lhs.base () <=> rhs.base (); } template <typename Pointer, typename DifferenceType> requires std::three_way_comparable<Pointer> constexpr auto operator<=> (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept (noexcept (lhs.base () <=> rhs.base ())) { return lhs.base () <=> rhs.base (); } template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> constexpr auto operator<=> (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept (noexcept (lhs.base () < rhs.base ()) && noexcept (rhs.base () < lhs.base ())) { using ordering = std::weak_ordering; return (lhs.base () < rhs.base ()) ? ordering::less : (rhs.base () < lhs.base ()) ? ordering::greater : ordering::equivalent; } template <typename Pointer, typename DifferenceType> constexpr auto operator<=> (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept (noexcept (lhs.base () < rhs.base ()) && noexcept (rhs.base () < lhs.base ())) { using ordering = std::weak_ordering; return (lhs.base () < rhs.base ()) ? ordering::less : (rhs.base () < lhs.base ()) ? ordering::greater : ordering::equivalent; } #else // Note: Passing this on from "Gaby" in stl_iterator.h -- templated // comparisons in generic code should have overloads for both // homogenous and heterogeneous types. This is because we get // ambiguous overload resolution when std::rel_ops is visible // (ie. `using namespace std::rel_ops`). template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> constexpr bool operator== (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept { return lhs.base () == rhs.base (); } template <typename Pointer, typename DifferenceType> constexpr bool operator== (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept { return lhs.base () == rhs.base (); } template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> constexpr bool operator!= (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept { return lhs.base () != rhs.base (); } template <typename Pointer, typename DifferenceType> constexpr bool operator!= (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept { return lhs.base () != rhs.base (); } template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> constexpr bool operator< (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept { return lhs.base () < rhs.base (); } template <typename Pointer, typename DifferenceType> constexpr bool operator< (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept { return lhs.base () < rhs.base (); } template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> constexpr bool operator>= (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept { return lhs.base () >= rhs.base (); } template <typename Pointer, typename DifferenceType> constexpr bool operator>= (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept { return lhs.base () >= rhs.base (); } template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> constexpr bool operator> (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept { return lhs.base () > rhs.base (); } template <typename Pointer, typename DifferenceType> constexpr bool operator> (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept { return lhs.base () > rhs.base (); } template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS, typename DifferenceTypeRHS> constexpr bool operator<= (const small_vector_iterator<PointerLHS, DifferenceTypeLHS>& lhs, const small_vector_iterator<PointerRHS, DifferenceTypeRHS>& rhs) noexcept { return lhs.base () <= rhs.base (); } template <typename Pointer, typename DifferenceType> constexpr bool operator<= (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept { return lhs.base () <= rhs.base (); } #endif template <typename PointerLHS, typename PointerRHS, typename DifferenceType> constexpr DifferenceType operator- (const small_vector_iterator<PointerLHS, DifferenceType>& lhs, const small_vector_iterator<PointerRHS, DifferenceType>& rhs) noexcept { return static_cast<DifferenceType> (lhs.base () - rhs.base ()); } template <typename Pointer, typename DifferenceType> constexpr DifferenceType operator- (const small_vector_iterator<Pointer, DifferenceType>& lhs, const small_vector_iterator<Pointer, DifferenceType>& rhs) noexcept { return static_cast<DifferenceType> (lhs.base () - rhs.base ()); } template <typename Pointer, typename DifferenceType> constexpr small_vector_iterator<Pointer, DifferenceType> operator+ (DifferenceType n, const small_vector_iterator<Pointer, DifferenceType>& it) noexcept { return it + n; } namespace detail { #ifndef GCH_LIB_IS_SWAPPABLE namespace small_vector_adl { using std::swap; template <typename T, typename Enable = void> struct is_nothrow_swappable : std::false_type { }; template <typename T> struct is_nothrow_swappable<T, decltype (swap (std::declval<T&> (), std::declval<T&> ()))> : std::integral_constant<bool, noexcept (swap (std::declval<T&> (), std::declval<T&> ()))> { }; } #endif template <typename T, unsigned InlineCapacity> class inline_storage { public: using value_ty = T; inline_storage (void) = default; inline_storage (const inline_storage&) = delete; inline_storage (inline_storage&&) noexcept = delete; inline_storage& operator= (const inline_storage&) = delete; inline_storage& operator= (inline_storage&&) noexcept = delete; ~inline_storage (void) = default; GCH_NODISCARD GCH_CPP14_CONSTEXPR value_ty * get_inline_ptr (void) noexcept { return static_cast<value_ty *> (static_cast<void *> (std::addressof (*m_data))); } private: union alignas (alignof (value_ty)) { unsigned char _[sizeof (value_ty)]; } m_data[InlineCapacity]; }; template <typename Allocator, bool AvailableForEBO = std::is_empty<Allocator>::value #ifdef GCH_LIB_IS_FINAL &&! std::is_final<Allocator>::value #endif // If you are using this with C++11 just don't use an allocator marked as final :P > class allocator_inliner; template <typename Allocator> class GCH_EMPTY_BASE allocator_inliner<Allocator, true> : private Allocator { public: // Note: The Allocator named requirements specify that copies do not throw. allocator_inliner (void) = default; allocator_inliner (const allocator_inliner&) noexcept = default; allocator_inliner (allocator_inliner&&) noexcept = default; allocator_inliner& operator= (const allocator_inliner&) noexcept = default; allocator_inliner& operator= (allocator_inliner&&) noexcept = default; ~allocator_inliner (void) = default; constexpr explicit allocator_inliner (const Allocator& alloc) noexcept : Allocator (alloc) { } GCH_CPP14_CONSTEXPR Allocator& allocator_ref (void) noexcept { return *this; } constexpr const Allocator& allocator_ref (void) const noexcept { return *this; } GCH_CPP20_CONSTEXPR void swap (allocator_inliner& other) { using std::swap; swap (static_cast<Allocator&> (*this), static_cast<Allocator&> (other)); } }; template <typename Allocator> class allocator_inliner<Allocator, false> { public: // Note: The Allocator named requirements specify that copies do not throw. allocator_inliner (void) = default; allocator_inliner (const allocator_inliner&) noexcept = default; allocator_inliner (allocator_inliner&&) noexcept = default; allocator_inliner& operator= (const allocator_inliner&) noexcept = default; allocator_inliner& operator= (allocator_inliner&&) noexcept = default; ~allocator_inliner (void) = default; GCH_CPP20_CONSTEXPR explicit allocator_inliner (const Allocator& alloc) noexcept : m_alloc (alloc) { } GCH_CPP14_CONSTEXPR Allocator& allocator_ref (void) noexcept { return m_alloc; } constexpr const Allocator& allocator_ref (void) const noexcept { return m_alloc; } GCH_CPP20_CONSTEXPR void swap (allocator_inliner& other) { using std::swap; swap (m_alloc, other.m_alloc); } private: Allocator m_alloc; }; template <typename Allocator> class GCH_EMPTY_BASE allocator_interface : public allocator_inliner<Allocator> { public: template <typename, typename = void> struct is_complete : std::false_type { }; template <typename U> struct is_complete<U, decltype (static_cast<void> (sizeof (U)))> : std::true_type { }; using size_type = typename std::allocator_traits<Allocator>::size_type; // If difference_type is larger than size_type then we need // to rectify that problem. using difference_type = typename std::conditional< ( static_cast<std::size_t> ((std::numeric_limits<size_type>::max) ()) < // less-than static_cast<std::size_t> ((std::numeric_limits< typename std::allocator_traits<Allocator>::difference_type>::max) ()) ), typename std::make_signed<size_type>::type, typename std::allocator_traits<Allocator>::difference_type>::type; private: using alloc_base = allocator_inliner<Allocator>; protected: using alloc_ty = Allocator; using alloc_traits = std::allocator_traits<alloc_ty>; using value_ty = typename alloc_traits::value_type; using ptr = typename alloc_traits::pointer; using cptr = typename alloc_traits::const_pointer; using vptr = typename alloc_traits::void_pointer; using cvptr = typename alloc_traits::const_void_pointer; // Select the fastest types larger than the user-facing types. These are only intended for // internal computations, and should not have any memory footprint visible to consumers. using size_ty = typename std::conditional< (sizeof (size_type) <= sizeof (std::uint8_t)), std::uint_fast8_t, typename std::conditional< (sizeof (size_type) <= sizeof (std::uint16_t)), std::uint_fast16_t, typename std::conditional< (sizeof (size_type) <= sizeof (std::uint32_t)), std::uint_fast32_t, typename std::conditional< (sizeof (size_type) <= sizeof (std::uint64_t)), std::uint_fast64_t, size_type >::type >::type >::type >::type; using diff_ty = typename std::conditional< (sizeof (difference_type) <= sizeof (std::int8_t)), std::int_fast8_t, typename std::conditional< (sizeof (difference_type) <= sizeof (std::int16_t)), std::int_fast16_t, typename std::conditional< (sizeof (difference_type) <= sizeof (std::int32_t)), std::int_fast32_t, typename std::conditional< (sizeof (difference_type) <= sizeof (std::int64_t)), std::int_fast64_t, difference_type >::type >::type >::type >::type; using alloc_base::allocator_ref; private: template <typename ...> using void_t = void; template <bool B> using bool_constant = std::integral_constant<bool, B>; template <typename V, typename Enable = void> struct is_trivially_destructible : std::false_type { }; template <typename V> struct is_trivially_destructible<V, typename std::enable_if<is_complete<V>::value>::type> : std::is_trivially_destructible<V> { }; template <typename Void, typename T, typename ...Args> struct is_trivially_constructible_impl : std::false_type { }; template <typename V, typename ...Args> struct is_trivially_constructible_impl< typename std::enable_if<is_complete<V>::value>::type, V, Args...> : std::is_trivially_constructible<V, Args...> { }; template <typename V, typename ...Args> struct is_trivially_constructible : is_trivially_constructible_impl<void, V, Args...> { }; template <typename T, typename Enable = void> struct underlying_if_enum { using type = T; }; template <typename T> struct underlying_if_enum<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::underlying_type<T> { }; template <typename T> using underlying_if_enum_t = typename underlying_if_enum<T>::type; template <typename, typename = void> struct has_ptr_traits_to_address : std::false_type { }; template <typename P> struct has_ptr_traits_to_address<P, void_t<decltype (std::pointer_traits<P>::to_address (std::declval<P> ()))>> : std::true_type { }; template <typename Void, typename A, typename V, typename ...Args> struct has_alloc_construct_check : std::false_type { }; template <typename A, typename V, typename ...Args> struct has_alloc_construct_check< void_t<decltype (std::declval<A&> ().construct (std::declval<V *> (), std::declval<Args> ()...))>, A, V, Args...> : std::true_type { }; template <typename Void, typename A, typename V, typename ...Args> struct has_alloc_construct_impl : std::false_type { }; template <typename A, typename V, typename ...Args> struct has_alloc_construct_impl<typename std::enable_if<is_complete<V>::value>::type, A, V, Args...> : has_alloc_construct_check<void, A, V, Args...> { }; template <typename A, typename V, typename ...Args> struct has_alloc_construct : has_alloc_construct_impl<void, A, V, Args...> { }; template <typename A, typename V, typename ...Args> struct must_use_alloc_construct : bool_constant<! std::is_same<A, std::allocator<V>>::value && has_alloc_construct<A, V, Args...>::value> { }; template <typename Void, typename A, typename V> struct has_alloc_destroy_impl : std::false_type { }; template <typename A, typename V> struct has_alloc_destroy_impl< void_t<decltype (std::declval<A&> ().destroy (std::declval<V *> ()))>, A, V> : std::true_type { }; template <typename A, typename V, typename Enable = void> struct has_alloc_destroy : std::false_type { }; template <typename A, typename V> struct has_alloc_destroy<A, V, typename std::enable_if<is_complete<V>::value>::type> : has_alloc_destroy_impl<void, A, V> { }; template <typename A, typename V> struct must_use_alloc_destroy : bool_constant<! std::is_same<A, std::allocator<V>>::value && has_alloc_destroy<A, V>::value> { }; public: allocator_interface (void) = default; // allocator_interface (const allocator_interface&) = impl; allocator_interface (allocator_interface&&) noexcept = default; allocator_interface& operator= (const allocator_interface&) noexcept = default; allocator_interface& operator= (allocator_interface&&) noexcept = default; ~allocator_interface (void) = default; GCH_CPP20_CONSTEXPR allocator_interface (const allocator_interface& other) : alloc_base (alloc_traits::select_on_container_copy_construction (other.allocator_ref ())) { } constexpr explicit allocator_interface (const alloc_ty& alloc) noexcept : alloc_base (alloc) { } template <bool Propagate = alloc_traits::propagate_on_container_copy_assignment::value, typename std::enable_if<! Propagate, bool>::type = false> GCH_CPP20_CONSTEXPR void maybe_copy (const allocator_interface&) noexcept { } template <bool Propagate = alloc_traits::propagate_on_container_copy_assignment::value, typename std::enable_if<Propagate, bool>::type = true> GCH_CPP20_CONSTEXPR void maybe_copy (const allocator_interface& other) noexcept (std::is_nothrow_copy_assignable<alloc_base>::value) { alloc_base::operator= (other); } template <bool Propagate = alloc_traits::propagate_on_container_move_assignment::value, typename std::enable_if<! Propagate, bool>::type = false> GCH_CPP20_CONSTEXPR void maybe_move (allocator_interface&&) noexcept { } template <bool Propagate = alloc_traits::propagate_on_container_move_assignment::value, typename std::enable_if<Propagate, bool>::type = true> GCH_CPP20_CONSTEXPR void maybe_move (allocator_interface&& other) noexcept (std::is_nothrow_move_assignable<alloc_base>::value) { alloc_base::operator= (std::move (other)); } template <bool Propagate = alloc_traits::propagate_on_container_swap::value, typename std::enable_if<! Propagate, bool>::type = false> GCH_CPP20_CONSTEXPR void maybe_swap (allocator_interface&) { } template <bool Propagate = alloc_traits::propagate_on_container_swap::value, typename std::enable_if<Propagate, bool>::type = true> GCH_CPP20_CONSTEXPR void maybe_swap (allocator_interface& other) { alloc_base::swap (other); } template <typename, typename, typename = void> struct is_memcpyable_integral : std::false_type { }; template <typename From, typename To> struct is_memcpyable_integral<From, To, typename std::enable_if<is_complete<From>::value>::type> { using from = underlying_if_enum_t<From>; using to = underlying_if_enum_t<To>; static constexpr bool value = (sizeof (from) == sizeof (to)) && (std::is_same<bool, from>::value == std::is_same<bool, to>::value) && std::is_integral<from>::value && std::is_integral<to>::value; }; template <typename From, typename To> struct is_convertible_pointer : bool_constant<std::is_pointer<From>::value && std::is_pointer<To>::value && std::is_convertible<From, To>::value> { }; // Memcpyable assignment. template <typename QualifiedFrom, typename QualifiedTo = value_ty, typename Enable = void> struct is_memcpyable : std::false_type { }; template <typename QualifiedFrom, typename QualifiedTo> struct is_memcpyable<QualifiedFrom, QualifiedTo, typename std::enable_if<is_complete<QualifiedFrom>::value>::type> { static_assert (! std::is_reference<QualifiedTo>::value, "QualifiedTo must not be a reference."); using from = typename std::remove_reference< typename std::remove_cv<QualifiedFrom>::type>::type; using to = typename std::remove_cv<QualifiedTo>::type; static constexpr bool value = std::is_trivially_assignable<QualifiedTo&, QualifiedFrom>::value && std::is_trivially_copyable<to>::value && ( std::is_same<typename std::remove_cv<from>::type, to>::value || is_memcpyable_integral<from, to>::value || is_convertible_pointer<from, to>::value); }; // Memcpyable construction. template <typename QualifiedFrom, typename QualifiedTo> struct is_uninitialized_memcpyable_impl { static_assert (! std::is_reference<QualifiedTo>::value, "QualifiedTo must not be a reference."); using from = typename std::remove_reference< typename std::remove_cv<QualifiedFrom>::type>::type; using to = typename std::remove_cv<QualifiedTo>::type; static constexpr bool value = std::is_trivially_constructible<QualifiedTo, QualifiedFrom>::value && std::is_trivially_copyable<to>::value && ( std::is_same<typename std::remove_cv<from>::type, to>::value || is_memcpyable_integral<from, to>::value || is_convertible_pointer<from, to>::value) && (! must_use_alloc_construct<alloc_ty, value_ty, from>::value &&! must_use_alloc_destroy<alloc_ty, value_ty>::value); }; template <typename To, typename ...Args> struct is_uninitialized_memcpyable : std::false_type { }; template <typename To, typename From> struct is_uninitialized_memcpyable<To, From> : is_uninitialized_memcpyable_impl<From, To> { }; template <typename Iterator> struct is_small_vector_iterator : std::false_type { }; template <typename ...Ts> struct is_small_vector_iterator<small_vector_iterator<Ts...>> : std::true_type { }; template <typename InputIt> struct is_contiguous_iterator : bool_constant< std::is_same<InputIt, ptr>::value || std::is_same<InputIt, cptr>::value || is_small_vector_iterator<InputIt>::value #ifdef GCH_LIB_CONCEPTS || std::contiguous_iterator<InputIt> #endif #ifdef GCH_STDLIB_INTEROP || std::is_same<InputIt, typename std::array<value_ty>::iterator>::value || std::is_same<InputIt, typename std::array<value_ty>::const_iterator>::value || (! std::is_same<value_ty, bool> && ( std::is_same<InputIt, typename std::vector<value_ty>::iterator>::value || std::is_same<InputIt, typename std::vector<value_ty>::const_iterator>::value) ) || std::is_same<InputIt, decltype (std::begin (std::declval<std::valarray<value_ty>&> ()))>::value || std::is_same<InputIt, decltype (std::begin (std::declval<const std::valarray<value_ty>&> ()))>::value #endif > { }; template <typename InputIt> struct is_memcpyable_iterator : bool_constant<is_memcpyable<decltype (*std::declval<InputIt> ())>::value && is_contiguous_iterator<InputIt>::value> { }; // Unwrap `move_iterator`s. template <typename InputIt> struct is_memcpyable_iterator<std::move_iterator<InputIt>> : is_memcpyable_iterator<InputIt> { }; template <typename InputIt, typename V = value_ty> struct is_uninitialized_memcpyable_iterator : bool_constant<is_uninitialized_memcpyable<V, decltype (*std::declval<InputIt> ())>::value && is_contiguous_iterator<InputIt>::value> { }; // unwrap move_iterators template <typename U, typename V> struct is_uninitialized_memcpyable_iterator<std::move_iterator<U>, V> : is_uninitialized_memcpyable_iterator<U, V> { }; #ifndef NDEBUG GCH_NORETURN static GCH_CPP20_CONSTEXPR void throw_range_length_error (void) { #ifdef GCH_EXCEPTIONS throw std::length_error ("The specified range is too long."); #else std::fprintf (stderr, "[gch::small_vector] The specified range is too long."); std::abort (); #endif } #endif static constexpr value_ty * to_address (value_ty *p) noexcept { static_assert (! std::is_function<value_ty>::value, "value_ty is a function pointer."); return p; } static constexpr const value_ty * to_address (const value_ty *p) noexcept { static_assert (! std::is_function<value_ty>::value, "value_ty is a function pointer."); return p; } template <typename Pointer, typename std::enable_if<has_ptr_traits_to_address<Pointer>::value>::type * = nullptr> static constexpr auto to_address (const Pointer& p) noexcept -> decltype (std::pointer_traits<Pointer>::to_address (p)) { return std::pointer_traits<Pointer>::to_address (p); } template <typename Pointer, typename std::enable_if<! has_ptr_traits_to_address<Pointer>::value>::type * = nullptr> static constexpr auto to_address (const Pointer& p) noexcept -> decltype (to_address (p.operator-> ())) { return to_address (p.operator-> ()); } template <typename Integer> GCH_NODISCARD static GCH_CONSTEVAL std::size_t numeric_max (void) noexcept { static_assert (0 <= (std::numeric_limits<Integer>::max) (), "Integer is nonpositive."); return static_cast<std::size_t> ((std::numeric_limits<Integer>::max) ()); } GCH_NODISCARD static GCH_CPP17_CONSTEXPR size_ty internal_range_length (cptr first, cptr last) noexcept { // This is guaranteed to be less than or equal to max size_ty. return static_cast<size_ty> (last - first); } template <typename RandomIt> GCH_NODISCARD static GCH_CPP17_CONSTEXPR size_ty external_range_length_impl (RandomIt first, RandomIt last, std::random_access_iterator_tag) { assert (0 <= (last - first) && "Invalid range."); const auto len = static_cast<std::size_t> (last - first); #ifndef NDEBUG if (numeric_max<size_ty> () < len) throw_range_length_error (); #endif return static_cast<size_ty> (len); } template <typename ForwardIt> GCH_NODISCARD static GCH_CPP17_CONSTEXPR size_ty external_range_length_impl (ForwardIt first, ForwardIt last, std::forward_iterator_tag) { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) { // Make sure constexpr doesn't get broken by `using namespace std::rel_ops`. typename std::iterator_traits<ForwardIt>::difference_type len = 0; for (; ! (first == last); ++first) ++len; assert (static_cast<std::size_t> (len) <= numeric_max<size_ty> ()); return static_cast<size_ty> (len); } #endif const auto len = static_cast<std::size_t> (std::distance (first, last)); #ifndef NDEBUG if (numeric_max<size_ty> () < len) throw_range_length_error (); #endif return static_cast<size_ty> (len); } template <typename ForwardIt, typename ItDiffT = typename std::iterator_traits<ForwardIt>::difference_type, typename std::enable_if<(numeric_max<size_ty> () < numeric_max<ItDiffT> ()), bool>::type = true> GCH_NODISCARD static GCH_CPP17_CONSTEXPR size_ty external_range_length (ForwardIt first, ForwardIt last) { using iterator_cat = typename std::iterator_traits<ForwardIt>::iterator_category; return external_range_length_impl (first, last, iterator_cat { }); } template <typename ForwardIt, typename ItDiffT = typename std::iterator_traits<ForwardIt>::difference_type, typename std::enable_if<! (numeric_max<size_ty> () < numeric_max<ItDiffT> ()), bool>::type = false> GCH_NODISCARD static GCH_CPP17_CONSTEXPR size_ty external_range_length (ForwardIt first, ForwardIt last) noexcept { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) { // Make sure constexpr doesn't get broken by `using namespace std::rel_ops`. size_ty len = 0; for (; ! (first == last); ++first) ++len; return len; } #endif return static_cast<size_ty> (std::distance (first, last)); } template <typename Iterator, typename IteratorDiffT = typename std::iterator_traits<Iterator>::difference_type, typename Integer = IteratorDiffT> GCH_NODISCARD static GCH_CPP17_CONSTEXPR Iterator unchecked_next (Iterator pos, Integer n = 1) noexcept { unchecked_advance (pos, static_cast<IteratorDiffT> (n)); return pos; } template <typename Iterator, typename IteratorDiffT = typename std::iterator_traits<Iterator>::difference_type, typename Integer = IteratorDiffT> GCH_NODISCARD static GCH_CPP17_CONSTEXPR Iterator unchecked_prev (Iterator pos, Integer n = 1) noexcept { unchecked_advance (pos, -static_cast<IteratorDiffT> (n)); return pos; } template <typename Iterator, typename IteratorDiffT = typename std::iterator_traits<Iterator>::difference_type, typename Integer = IteratorDiffT> static GCH_CPP17_CONSTEXPR void unchecked_advance (Iterator& pos, Integer n) noexcept { std::advance (pos, static_cast<IteratorDiffT> (n)); } GCH_NODISCARD GCH_CPP20_CONSTEXPR size_ty get_max_size (void) const noexcept { // This is protected from max/min macros. return (std::min) (static_cast<size_ty> (alloc_traits::max_size (allocator_ref ())), static_cast<size_ty> (numeric_max<difference_type> ())); } GCH_NODISCARD GCH_CPP20_CONSTEXPR ptr allocate (size_ty n) { return alloc_traits::allocate (allocator_ref (), static_cast<size_type> (n)); } GCH_NODISCARD GCH_CPP20_CONSTEXPR ptr allocate_with_hint (size_ty n, cptr hint) { return alloc_traits::allocate (allocator_ref (), static_cast<size_type> (n), hint); } GCH_CPP20_CONSTEXPR void deallocate (ptr p, size_ty n) { alloc_traits::deallocate (allocator_ref (), to_address (p), static_cast<size_type> (n)); } template <typename U, typename std::enable_if< is_uninitialized_memcpyable<value_ty, U>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void construct (ptr p, U&& val) noexcept { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) { alloc_traits::construct (allocator_ref (), to_address (p), std::forward<U> (val)); return; } #endif std::memcpy (to_address (p), &val, sizeof (value_ty)); } // This is basically alloc_traits::construct, and is defined so that we // can replicate C++20 behavior in the other overload. template <typename A = alloc_ty, typename V = value_ty, typename ...Args, typename std::enable_if<( sizeof...(Args) != 1 ||! is_uninitialized_memcpyable<V, Args...>::value) && has_alloc_construct<A, V, Args...>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void construct (ptr p, Args&&... args) noexcept (noexcept (alloc_traits::construct (std::declval<alloc_ty&> (), std::declval<value_ty *> (), std::forward<Args> (args)...))) { alloc_traits::construct (allocator_ref (), to_address (p), std::forward<Args> (args)...); } template <typename A = alloc_ty, typename V = value_ty, typename ...Args, void_t<typename std::enable_if<( sizeof...(Args) != 1 ||! is_uninitialized_memcpyable<V, Args...>::value) &&! has_alloc_construct<A, V, Args...>::value>::type, decltype (::new (std::declval<void *> ()) V (std::declval<Args> ()...)) > * = nullptr> GCH_CPP20_CONSTEXPR void construct (ptr p, Args&&... args) noexcept (noexcept (::new (std::declval<void *> ()) value_ty (std::declval<Args> ()...))) { construct_at (to_address (p), std::forward<Args> (args)...); } template <typename A = alloc_ty, typename V = value_ty, typename std::enable_if<is_trivially_destructible<V>::value &&! must_use_alloc_destroy<A, V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void destroy (ptr) const noexcept { } template <typename A = alloc_ty, typename V = value_ty, typename std::enable_if<(! is_trivially_destructible<V>::value || must_use_alloc_destroy<A, V>::value) && has_alloc_destroy<A, V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void destroy (ptr p) noexcept { alloc_traits::destroy (allocator_ref (), to_address (p)); } // This is defined so that we match C++20 behavior in all cases. template <typename A = alloc_ty, typename V = value_ty, typename std::enable_if<(! is_trivially_destructible<V>::value || must_use_alloc_destroy<A, V>::value) &&! has_alloc_destroy<A, V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void destroy (ptr p) noexcept { destroy_at (to_address (p)); } template <typename A = alloc_ty, typename V = value_ty, typename std::enable_if<is_trivially_destructible<V>::value &&! must_use_alloc_destroy<A, V>::value>::type * = nullptr> GCH_CPP14_CONSTEXPR void destroy_range (ptr, ptr) const noexcept { } template <typename A = alloc_ty, typename V = value_ty, typename std::enable_if<! is_trivially_destructible<V>::value || must_use_alloc_destroy<A, V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void destroy_range (ptr first, ptr last) noexcept { for (; ! (first == last); ++first) destroy (first); } // This is used if the type is trivially copyable, we use the standard // allocator, and `ForwardIt` is a contiguous iterator. template <typename ForwardIt, typename std::enable_if< is_uninitialized_memcpyable_iterator<ForwardIt>::value, bool>::type = true> GCH_CPP20_CONSTEXPR ptr uninitialized_copy (ForwardIt first, ForwardIt last, ptr dest) noexcept { static_assert (std::is_constructible<value_ty, decltype (*first)>::value, "`value_type` must be copy constructible."); #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) return default_uninitialized_copy (first, last, dest); #endif const size_ty num_copy = external_range_length (first, last); if (num_copy != 0) std::memcpy (to_address (dest), to_address (first), num_copy * sizeof (value_ty)); return unchecked_next (dest, num_copy); } template <typename ForwardIt, typename std::enable_if< is_uninitialized_memcpyable_iterator<ForwardIt>::value, bool>::type = true> GCH_CPP20_CONSTEXPR ptr uninitialized_copy (std::move_iterator<ForwardIt> first, std::move_iterator<ForwardIt> last, ptr dest) noexcept { return uninitialized_copy (first.base (), last.base (), dest); } template <typename InputIt, typename std::enable_if< ! is_uninitialized_memcpyable_iterator<InputIt>::value, bool>::type = false> GCH_CPP20_CONSTEXPR ptr uninitialized_copy (InputIt first, InputIt last, ptr d_first) { return default_uninitialized_copy (first, last, d_first); } template <typename InputIt> GCH_CPP20_CONSTEXPR ptr default_uninitialized_copy (InputIt first, InputIt last, ptr d_first) { ptr d_last = d_first; GCH_TRY { // Note: Not != because `using namespace std::rel_ops` can break constexpr. for (; ! (first == last); ++first, static_cast<void> (++d_last)) construct (d_last, *first); return d_last; } GCH_CATCH (...) { destroy_range (d_first, d_last); GCH_THROW; } } template <typename A = alloc_ty, typename V = value_ty, typename std::enable_if<is_trivially_constructible<V>::value &&! must_use_alloc_construct<A, V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR ptr uninitialized_value_construct (ptr first, ptr last) { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) return default_uninitialized_value_construct (first, last); #endif std::fill (first, last, value_ty ()); return last; } template <typename A = alloc_ty, typename V = value_ty, typename std::enable_if<! is_trivially_constructible<V>::value || must_use_alloc_construct<A, V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR ptr uninitialized_value_construct (ptr first, ptr last) { return default_uninitialized_value_construct (first, last); } GCH_CPP20_CONSTEXPR ptr default_uninitialized_value_construct (ptr first, ptr last) { ptr curr = first; GCH_TRY { for (; ! (curr == last); ++curr) construct (curr); return curr; } GCH_CATCH (...) { destroy_range (first, curr); GCH_THROW; } } GCH_CPP20_CONSTEXPR ptr uninitialized_fill (ptr first, ptr last) { return uninitialized_value_construct (first, last); } GCH_CPP20_CONSTEXPR ptr uninitialized_fill (ptr first, ptr last, const value_ty& val) { ptr curr = first; GCH_TRY { for (; ! (curr == last); ++curr) construct (curr, val); return curr; } GCH_CATCH (...) { destroy_range (first, curr); GCH_THROW; } } private: // If value_ty is an array, replicate C++20 behavior (I don't think that value_ty can // actually be an array because of the Erasable requirement, but there shouldn't // be any runtime cost for being defensive here). template <typename V = value_ty, typename std::enable_if<std::is_array<V>::value, bool>::type = true> static GCH_CPP20_CONSTEXPR void destroy_at (value_ty *p) noexcept { for (auto& e : *p) destroy_at (std::addressof (e)); } template <typename V = value_ty, typename std::enable_if<! std::is_array<V>::value, bool>::type = false> static GCH_CPP20_CONSTEXPR void destroy_at (value_ty *p) noexcept { p->~value_ty (); } template <typename V = value_ty, typename ...Args> static GCH_CPP20_CONSTEXPR auto construct_at (value_ty *p, Args&&... args) noexcept (noexcept (::new (std::declval<void *> ()) V (std::declval<Args> ()...))) -> decltype (::new (std::declval<void *> ()) V (std::declval<Args> ()...)) { #if defined (GCH_LIB_IS_CONSTANT_EVALUATED) && defined (GCH_LIB_CONSTEXPR_MEMORY) if (std::is_constant_evaluated ()) return std::construct_at (p, std::forward<Args> (args)...); #endif void *vp = const_cast<void *> (static_cast<const volatile void *> (p)); return ::new (vp) value_ty (std::forward<Args>(args)...); } }; template <typename Pointer, typename SizeT> class small_vector_data_base { public: using ptr = Pointer; using size_ty = SizeT; small_vector_data_base (void) = default; small_vector_data_base (const small_vector_data_base&) = default; small_vector_data_base (small_vector_data_base&&) noexcept = default; small_vector_data_base& operator= (const small_vector_data_base&) = default; small_vector_data_base& operator= (small_vector_data_base&&) noexcept = default; ~small_vector_data_base (void) = default; constexpr ptr data_ptr (void) const noexcept { return m_data_ptr; } constexpr size_ty capacity (void) const noexcept { return m_capacity; } constexpr size_ty size (void) const noexcept { return m_size; } GCH_CPP20_CONSTEXPR void set_data_ptr (ptr data_ptr) noexcept { m_data_ptr = data_ptr; } GCH_CPP20_CONSTEXPR void set_capacity (size_ty capacity) noexcept { m_capacity = capacity; } GCH_CPP20_CONSTEXPR void set_size (size_ty size) noexcept { m_size = size; } GCH_CPP20_CONSTEXPR void set (ptr data_ptr, size_ty capacity, size_ty size) { m_data_ptr = data_ptr; m_capacity = capacity; m_size = size; } GCH_CPP20_CONSTEXPR void swap_data_ptr (small_vector_data_base& other) noexcept { using std::swap; swap (m_data_ptr, other.m_data_ptr); } GCH_CPP20_CONSTEXPR void swap_capacity (small_vector_data_base& other) noexcept { using std::swap; swap (m_capacity, other.m_capacity); } GCH_CPP20_CONSTEXPR void swap_size (small_vector_data_base& other) noexcept { using std::swap; swap (m_size, other.m_size); } GCH_CPP20_CONSTEXPR void swap (small_vector_data_base& other) noexcept { using std::swap; swap (m_data_ptr, other.m_data_ptr); swap (m_capacity, other.m_capacity); swap (m_size, other.m_size); } private: ptr m_data_ptr; size_ty m_capacity; size_ty m_size; }; template <typename Pointer, typename SizeT, typename T, unsigned InlineCapacity> class small_vector_data : public small_vector_data_base<Pointer, SizeT> { public: small_vector_data (void) = default; small_vector_data (const small_vector_data&) = delete; small_vector_data (small_vector_data&&) noexcept = delete; small_vector_data& operator= (const small_vector_data&) = delete; small_vector_data& operator= (small_vector_data&&) noexcept = delete; ~small_vector_data (void) = default; GCH_CPP14_CONSTEXPR T * storage (void) noexcept { return m_storage.get_inline_ptr (); } private: inline_storage<T, InlineCapacity> m_storage; }; template <typename Pointer, typename SizeT, typename T> class GCH_EMPTY_BASE small_vector_data<Pointer, SizeT, T, 0> : public small_vector_data_base<Pointer, SizeT> { public: small_vector_data (void) = default; small_vector_data (const small_vector_data&) = delete; small_vector_data (small_vector_data&&) noexcept = delete; small_vector_data& operator= (const small_vector_data&) = delete; small_vector_data& operator= (small_vector_data&&) noexcept = delete; ~small_vector_data (void) = default; GCH_CPP14_CONSTEXPR T * storage (void) noexcept { return nullptr; } }; template <typename Allocator, unsigned InlineCapacity> class small_vector_base : public allocator_interface<Allocator> { public: using size_type = typename allocator_interface<Allocator>::size_type; using difference_type = typename allocator_interface<Allocator>::difference_type; template <typename SameAllocator, unsigned DifferentInlineCapacity> friend class small_vector_base; protected: using alloc_interface = allocator_interface<Allocator>; using alloc_traits = typename alloc_interface::alloc_traits; using alloc_ty = Allocator; using value_ty = typename alloc_interface::value_ty; using ptr = typename alloc_interface::ptr; using cptr = typename alloc_interface::cptr; using size_ty = typename alloc_interface::size_ty; using diff_ty = typename alloc_interface::diff_ty; static_assert (alloc_interface::template is_complete<value_ty>::value || InlineCapacity == 0, "`value_type` must be complete for instantiation of a non-zero number " "of inline elements."); template <typename T> using is_complete = typename alloc_interface::template is_complete<T>; using alloc_interface::allocator_ref; using alloc_interface::construct; using alloc_interface::deallocate; using alloc_interface::destroy; using alloc_interface::destroy_range; using alloc_interface::external_range_length; using alloc_interface::get_max_size; using alloc_interface::internal_range_length; using alloc_interface::to_address; using alloc_interface::unchecked_advance; using alloc_interface::unchecked_next; using alloc_interface::unchecked_prev; using alloc_interface::uninitialized_copy; using alloc_interface::uninitialized_fill; using alloc_interface::uninitialized_value_construct; template <typename Integer> GCH_NODISCARD static GCH_CONSTEVAL std::size_t numeric_max (void) noexcept { return alloc_interface::template numeric_max<Integer> (); } GCH_NODISCARD static GCH_CONSTEVAL size_ty get_inline_capacity (void) noexcept { return static_cast<size_ty> (InlineCapacity); } template <typename ...> using void_t = void; template <bool B> using bool_constant = std::integral_constant<bool, B>; template <typename Void, typename AI, typename V, typename ...Args> struct is_emplace_constructible_impl : std::false_type { using nothrow = std::false_type; }; template <typename AI, typename V, typename ...Args> struct is_emplace_constructible_impl< void_t<typename std::enable_if<is_complete<V>::value>::type, decltype (std::declval<AI&> ().construct (std::declval<V *> (), std::declval<Args> ()...))>, AI, V, Args...> : std::true_type { using nothrow = bool_constant<noexcept (std::declval<AI&> ().construct (std::declval<V *> (), std::declval<Args> ()...))>; }; template <typename ...Args> struct is_emplace_constructible : is_emplace_constructible_impl<void, alloc_interface, value_ty, Args...> { }; template <typename ...Args> struct is_nothrow_emplace_constructible : is_emplace_constructible_impl<void, alloc_interface, value_ty, Args...>::nothrow { }; template <typename V = value_ty> struct is_explicitly_move_insertable : is_emplace_constructible<V&&> { }; template <typename V = value_ty> struct is_explicitly_nothrow_move_insertable : is_nothrow_emplace_constructible<V&&> { }; template <typename V = value_ty> struct is_explicitly_copy_insertable : std::integral_constant<bool, is_emplace_constructible<V&>::value && is_emplace_constructible<const V&>::value> { }; template <typename V = value_ty> struct is_explicitly_nothrow_copy_insertable : std::integral_constant<bool, is_nothrow_emplace_constructible<V&>::value && is_nothrow_emplace_constructible<const V&>::value> { }; template <typename AI, typename Enable = void> struct is_eraseable : std::false_type { }; template <typename AI> struct is_eraseable<AI, void_t<decltype (std::declval<AI&> ().destroy (std::declval<value_ty *> ()))>> : std::true_type { }; template <typename V> struct relocate_with_move #ifdef GCH_NO_STRONG_EXCEPTION_GUARANTEES : std::true_type #else : bool_constant<std::is_nothrow_move_constructible<V>::value ||! is_explicitly_copy_insertable<V>::value> #endif { }; template <typename A> struct allocations_are_movable : bool_constant<std::is_same<std::allocator<value_ty>, A>::value || std::allocator_traits<A>::propagate_on_container_move_assignment::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<A>::is_always_equal::value #endif > { }; template <typename A> struct allocations_are_swappable : bool_constant<std::is_same<std::allocator<value_ty>, A>::value || std::allocator_traits<A>::propagate_on_container_swap::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<A>::is_always_equal::value #endif > { }; template <typename ...Args> using is_memcpyable = typename alloc_interface::template is_memcpyable<Args...>; template <typename ...Args> using is_memcpyable_iterator = typename alloc_interface::template is_memcpyable_iterator<Args...>; GCH_NORETURN static GCH_CPP20_CONSTEXPR void throw_index_error (void) { #ifdef GCH_EXCEPTIONS throw std::out_of_range ("The requested index was out of range."); #else std::fprintf (stderr, "[gch::small_vector] The requested index was out of range.\n"); std::abort (); #endif } GCH_NORETURN static GCH_CPP20_CONSTEXPR void throw_allocation_size_error (void) { #ifdef GCH_EXCEPTIONS throw std::length_error ("The required allocation exceeds the maximum size."); #else std::fprintf ( stderr, "[gch::small_vector] The required allocation exceeds the maximum size.\n"); std::abort (); #endif } GCH_NODISCARD GCH_CPP14_CONSTEXPR ptr ptr_cast (const small_vector_iterator<cptr, diff_ty>& it) noexcept { return unchecked_next (begin_ptr (), it.base () - begin_ptr ()); } private: class stack_temporary { public: stack_temporary (void) = delete; stack_temporary (const stack_temporary&) = delete; stack_temporary (stack_temporary&&) noexcept = delete; stack_temporary& operator= (const stack_temporary&) = delete; stack_temporary& operator= (stack_temporary&&) noexcept = delete; // ~stack_temporary (void) = impl; template <typename ...Args> GCH_CPP20_CONSTEXPR explicit stack_temporary (alloc_interface& alloc_iface, Args&&... args) : m_interface (alloc_iface) { m_interface.construct (get_pointer (), std::forward<Args> (args)...); } GCH_CPP20_CONSTEXPR ~stack_temporary (void) { m_interface.destroy (get_pointer ()); } GCH_NODISCARD GCH_CPP20_CONSTEXPR const value_ty& get (void) const noexcept { return *get_pointer (); } GCH_NODISCARD GCH_CPP20_CONSTEXPR value_ty&& release (void) noexcept { return std::move (*get_pointer ()); } private: GCH_NODISCARD GCH_CPP20_CONSTEXPR cptr get_pointer (void) const noexcept { return static_cast<cptr> (static_cast<const void *> (std::addressof (m_data))); } GCH_NODISCARD GCH_CPP20_CONSTEXPR ptr get_pointer (void) noexcept { return static_cast<ptr> (static_cast<void *> (std::addressof (m_data))); } alloc_interface& m_interface; alignas (alignof (value_ty)) unsigned char m_data[sizeof (value_ty)]; }; #ifdef GCH_LIB_IS_CONSTANT_EVALUATED class heap_temporary { public: heap_temporary (void) = delete; heap_temporary (const heap_temporary&) = delete; heap_temporary (heap_temporary&&) noexcept = delete; heap_temporary& operator= (const heap_temporary&) = delete; heap_temporary& operator= (heap_temporary&&) noexcept = delete; // ~heap_temporary (void) = impl; template <typename ...Args> GCH_CPP20_CONSTEXPR explicit heap_temporary (alloc_interface& alloc_iface, Args&&... args) : m_interface (alloc_iface), m_data_ptr (alloc_iface.allocate (sizeof (value_ty))) { GCH_TRY { m_interface.construct (m_data_ptr, std::forward<Args> (args)...); } GCH_CATCH (...) { m_interface.deallocate (m_data_ptr, sizeof (value_ty)); GCH_THROW; } } GCH_CPP20_CONSTEXPR ~heap_temporary (void) { m_interface.destroy (m_data_ptr); m_interface.deallocate (m_data_ptr, sizeof (value_ty)); } GCH_NODISCARD GCH_CPP20_CONSTEXPR const value_ty& get (void) const noexcept { return *m_data_ptr; } GCH_NODISCARD GCH_CPP20_CONSTEXPR value_ty&& release (void) noexcept { return std::move (*m_data_ptr); } private: alloc_interface& m_interface; ptr m_data_ptr; }; #endif GCH_CPP20_CONSTEXPR void wipe (void) { destroy_range (begin_ptr (), end_ptr ()); if (has_allocation ()) deallocate (data_ptr (), get_capacity ()); } GCH_CPP20_CONSTEXPR void set_data_ptr (ptr data_ptr) noexcept { m_data.set_data_ptr (data_ptr); } GCH_CPP20_CONSTEXPR void set_capacity (size_ty capacity) noexcept { m_data.set_capacity (static_cast<size_type> (capacity)); } GCH_CPP20_CONSTEXPR void set_size (size_ty size) noexcept { m_data.set_size (static_cast<size_type> (size)); } GCH_CPP20_CONSTEXPR void set_data (ptr data_ptr, size_ty capacity, size_ty size) noexcept { m_data.set (data_ptr, static_cast<size_type> (capacity), static_cast<size_type> (size)); } GCH_CPP20_CONSTEXPR void swap_data_ptr (small_vector_base& other) noexcept { m_data.swap_data_ptr (other.m_data); } GCH_CPP20_CONSTEXPR void swap_capacity (small_vector_base& other) noexcept { m_data.swap_capacity (other.m_data); } GCH_CPP20_CONSTEXPR void swap_size (small_vector_base& other) noexcept { m_data.swap_size (other.m_data); } GCH_CPP20_CONSTEXPR void swap_allocation (small_vector_base& other) noexcept { m_data.swap (other.m_data); } GCH_CPP20_CONSTEXPR void reset_data (ptr data_ptr, size_ty capacity, size_ty size) { wipe (); m_data.set (data_ptr, static_cast<size_type> (capacity), static_cast<size_type> (size)); } GCH_CPP20_CONSTEXPR void increase_size (size_ty n) noexcept { m_data.set_size (get_size () + n); } GCH_CPP20_CONSTEXPR void decrease_size (size_ty n) noexcept { m_data.set_size (get_size () - n); } GCH_CPP20_CONSTEXPR ptr unchecked_allocate (size_ty n) { assert (InlineCapacity < n && "Allocated capacity should be greater than InlineCapacity."); return alloc_interface::allocate (n); } GCH_CPP20_CONSTEXPR ptr unchecked_allocate (size_ty n, cptr hint) { assert (InlineCapacity < n && "Allocated capacity should be greater than InlineCapacity."); return alloc_interface::allocate_with_hint (n, hint); } GCH_CPP20_CONSTEXPR ptr checked_allocate (size_ty n) { if (get_max_size () < n) throw_allocation_size_error (); return unchecked_allocate (n); } protected: GCH_NODISCARD GCH_CPP14_CONSTEXPR size_ty unchecked_calculate_new_capacity (const size_ty minimum_required_capacity) const noexcept { const size_ty current_capacity = get_capacity (); assert (current_capacity < minimum_required_capacity); if (get_max_size () - current_capacity <= current_capacity) return get_max_size (); // Note: This growth factor might be theoretically superior, but in testing it falls flat: // size_ty new_capacity = current_capacity + (current_capacity / 2); const size_ty new_capacity = 2 * current_capacity; if (new_capacity < minimum_required_capacity) return minimum_required_capacity; return new_capacity; } GCH_NODISCARD GCH_CPP14_CONSTEXPR size_ty checked_calculate_new_capacity (const size_ty minimum_required_capacity) const { if (get_max_size () < minimum_required_capacity) throw_allocation_size_error (); return unchecked_calculate_new_capacity (minimum_required_capacity); } template <unsigned I> GCH_CPP20_CONSTEXPR small_vector_base& copy_assign_default (const small_vector_base<Allocator, I>& other) { if (get_capacity () < other.get_size ()) { // Reallocate. size_ty new_capacity = unchecked_calculate_new_capacity (other.get_size ()); ptr new_data_ptr = unchecked_allocate (new_capacity, other.allocation_end_ptr ()); GCH_TRY { uninitialized_copy (other.begin_ptr (), other.end_ptr (), new_data_ptr); } GCH_CATCH (...) { deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, other.get_size ()); } else { if (get_size () < other.get_size ()) { // No reallocation, partially in uninitialized space. std::copy_n (other.begin_ptr (), get_size (), begin_ptr ()); uninitialized_copy ( unchecked_next (other.begin_ptr (), get_size ()), other.end_ptr (), end_ptr ()); } else { destroy_range (copy_range (other.begin_ptr (), other.end_ptr (), begin_ptr ()), end_ptr ()); } // data_ptr and capacity do not change in this case. set_size (other.get_size ()); } alloc_interface::maybe_copy (other); return *this; } template <unsigned I, typename AT = alloc_traits, typename std::enable_if<AT::propagate_on_container_copy_assignment::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL &&! AT::is_always_equal::value #endif >::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base& copy_assign (const small_vector_base<Allocator, I>& other) { if (other.allocator_ref () == allocator_ref ()) return copy_assign_default (other); if (InlineCapacity < other.get_size ()) { // Avoid using `select_on_container_copy_construction` here. alloc_interface new_alloc (other.allocator_ref ()); const size_ty new_capacity = other.get_size (); const ptr new_data_ptr = new_alloc.allocate_with_hint ( new_capacity, other.allocation_end_ptr ()); GCH_TRY { uninitialized_copy (other.begin_ptr (), other.end_ptr (), new_data_ptr); } GCH_CATCH (...) { new_alloc.deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, other.get_size ()); alloc_interface::maybe_copy (new_alloc); } else { if (has_allocation ()) { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED ptr new_data_ptr; if (std::is_constant_evaluated ()) { alloc_interface new_alloc (other); new_data_ptr = new_alloc.allocate (InlineCapacity); } else new_data_ptr = storage_ptr (); #else const ptr new_data_ptr = storage_ptr (); #endif uninitialized_copy (other.begin_ptr (), other.end_ptr (), new_data_ptr); destroy_range (begin_ptr (), end_ptr ()); deallocate (data_ptr (), get_capacity ()); set_data_ptr (new_data_ptr); set_capacity (InlineCapacity); } else if (get_size () < other.get_size ()) { std::copy_n (other.begin_ptr (), get_size (), begin_ptr ()); uninitialized_copy ( unchecked_next (other.begin_ptr (), get_size ()), other.end_ptr (), end_ptr ()); } else { destroy_range (copy_range (other.begin_ptr (), other.end_ptr (), begin_ptr ()), end_ptr ()); } set_size (other.get_size ()); alloc_interface::maybe_copy (other); } return *this; } template <unsigned I, typename AT = alloc_traits, typename std::enable_if<! AT::propagate_on_container_copy_assignment::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || AT::is_always_equal::value #endif >::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base& copy_assign (const small_vector_base<Allocator, I>& other) { return copy_assign_default (other); } template <unsigned I> GCH_CPP20_CONSTEXPR void move_allocation_pointer (small_vector_base<alloc_ty, I>&& other) noexcept { reset_data (other.data_ptr (), other.get_capacity (), other.get_size ()); other.set_default (); } template <unsigned N = InlineCapacity, typename std::enable_if<N == 0>::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base& move_assign_default (small_vector_base&& other) noexcept { move_allocation_pointer (std::move (other)); alloc_interface::maybe_move (std::move (other)); return *this; } template <unsigned LessEqualI, typename std::enable_if<(LessEqualI <= InlineCapacity)>::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base& move_assign_default (small_vector_base<Allocator, LessEqualI>&& other) noexcept (std::is_nothrow_move_assignable<value_ty>::value && std::is_nothrow_move_constructible<value_ty>::value) { // We only move the allocation pointer over if it has strictly greater capacity than // the inline capacity of `*this` because allocations can never have a smaller capacity // than the inline capacity. if (InlineCapacity < other.get_capacity ()) move_allocation_pointer (std::move (other)); else { // We are guaranteed to have sufficient capacity to store the elements. if (InlineCapacity < get_capacity ()) { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED ptr new_data_ptr; if (std::is_constant_evaluated ()) new_data_ptr = other.allocate (InlineCapacity); else new_data_ptr = storage_ptr (); #else const ptr new_data_ptr = storage_ptr (); #endif uninitialized_move (other.begin_ptr (), other.end_ptr (), new_data_ptr); destroy_range (begin_ptr (), end_ptr ()); deallocate (data_ptr (), get_capacity ()); set_data_ptr (new_data_ptr); set_capacity (InlineCapacity); } else if (get_size () < other.get_size ()) { // There are more elements in `other`. // Overwrite the existing range and uninitialized move the rest. ptr other_pivot = unchecked_next (other.begin_ptr (), get_size ()); std::move (other.begin_ptr (), other_pivot, begin_ptr ()); uninitialized_move (other_pivot, other.end_ptr (), end_ptr ()); } else { // There are the same number or fewer elements in `other`. // Overwrite part of the existing range and destroy the rest. ptr new_end = std::move (other.begin_ptr (), other.end_ptr (), begin_ptr ()); destroy_range (new_end, end_ptr ()); } set_size (other.get_size ()); // Note: We do not need to deallocate any allocations in `other` because the value of // an object meeting the Allocator named requirements does not change value after // a move. } alloc_interface::maybe_move (std::move (other)); return *this; } template <unsigned GreaterI, typename std::enable_if<(InlineCapacity < GreaterI)>::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base& move_assign_default (small_vector_base<Allocator, GreaterI>&& other) { if (other.has_allocation ()) move_allocation_pointer (std::move (other)); else if (get_capacity () < other.get_size () || (has_allocation () && ! (other.allocator_ref () == allocator_ref ()))) { // Reallocate. // The compiler should be able to optimize this. size_ty new_capacity = get_capacity () < other.get_size () ? unchecked_calculate_new_capacity (other.get_size ()) : get_capacity (); ptr new_data_ptr = other.allocate_with_hint (new_capacity, other.allocation_end_ptr ()); GCH_TRY { uninitialized_move (other.begin_ptr (), other.end_ptr (), new_data_ptr); } GCH_CATCH (...) { other.deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, other.get_size ()); } else { if (get_size () < other.get_size ()) { // There are more elements in `other`. // Overwrite the existing range and uninitialized move the rest. ptr other_pivot = unchecked_next (other.begin_ptr (), get_size ()); std::move (other.begin_ptr (), other_pivot, begin_ptr ()); uninitialized_move (other_pivot, other.end_ptr (), end_ptr ()); } else { // fewer elements in other // overwrite part of the existing range and destroy the rest ptr new_end = std::move (other.begin_ptr (), other.end_ptr (), begin_ptr ()); destroy_range (new_end, end_ptr ()); } // `data_ptr` and `capacity` do not change in this case. set_size (other.get_size ()); } alloc_interface::maybe_move (std::move (other)); return *this; } template <unsigned I> GCH_CPP20_CONSTEXPR small_vector_base& move_assign_unequal_no_propagate (small_vector_base<Allocator, I>&& other) { if (get_capacity () < other.get_size ()) { // Reallocate. size_ty new_capacity = unchecked_calculate_new_capacity (other.get_size ()); ptr new_data_ptr = unchecked_allocate (new_capacity, other.allocation_end_ptr ()); GCH_TRY { uninitialized_move (other.begin_ptr (), other.end_ptr (), new_data_ptr); } GCH_CATCH (...) { deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, other.get_size ()); } else { if (get_size () < other.get_size ()) { // There are more elements in `other`. // Overwrite the existing range and uninitialized move the rest. ptr other_pivot = unchecked_next (other.begin_ptr (), get_size ()); std::move (other.begin_ptr (), other_pivot, begin_ptr ()); uninitialized_move (other_pivot, other.end_ptr (), end_ptr ()); } else { // There are fewer elements in `other`. // Overwrite part of the existing range and destroy the rest. destroy_range ( std::move (other.begin_ptr (), other.end_ptr (), begin_ptr ()), end_ptr ()); } // data_ptr and capacity do not change in this case set_size (other.get_size ()); } alloc_interface::maybe_move (std::move (other)); return *this; } template <unsigned I, typename A = alloc_ty, typename std::enable_if<allocations_are_movable<A>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base& move_assign (small_vector_base<Allocator, I>&& other) noexcept (noexcept ( std::declval<small_vector_base&> ().move_assign_default (std::move (other)))) { return move_assign_default (std::move (other)); } template <unsigned I, typename A = alloc_ty, typename std::enable_if<! allocations_are_movable<A>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base& move_assign (small_vector_base<Allocator, I>&& other) { if (other.allocator_ref () == allocator_ref ()) return move_assign_default (std::move (other)); return move_assign_unequal_no_propagate (std::move (other)); } template <unsigned I = InlineCapacity, typename std::enable_if<I == 0>::type * = nullptr> GCH_CPP20_CONSTEXPR void move_initialize (small_vector_base&& other) noexcept { set_data (other.data_ptr (), other.get_capacity (), other.get_size ()); other.set_default (); } template <unsigned LessEqualI, typename std::enable_if<(LessEqualI <= InlineCapacity)>::type * = nullptr> GCH_CPP20_CONSTEXPR void move_initialize (small_vector_base<Allocator, LessEqualI>&& other) noexcept (std::is_nothrow_move_constructible<value_ty>::value) { if (InlineCapacity < other.get_capacity ()) { set_data (other.data_ptr (), other.get_capacity (), other.get_size ()); other.set_default (); } else { set_to_inline_storage (); uninitialized_move (other.begin_ptr (), other.end_ptr (), data_ptr ()); set_size (other.get_size ()); } } template <unsigned GreaterI, typename std::enable_if<(InlineCapacity < GreaterI)>::type * = nullptr> GCH_CPP20_CONSTEXPR void move_initialize (small_vector_base<Allocator, GreaterI>&& other) { if (other.has_allocation ()) { set_data (other.data_ptr (), other.get_capacity (), other.get_size ()); other.set_default (); } else { if (InlineCapacity < other.get_size ()) { // We may throw in this case. set_data_ptr (unchecked_allocate (other.get_size (), other.allocation_end_ptr ())); set_capacity (other.get_size ()); GCH_TRY { uninitialized_move (other.begin_ptr (), other.end_ptr (), data_ptr ()); } GCH_CATCH (...) { deallocate (data_ptr (), get_capacity ()); GCH_THROW; } } else { set_to_inline_storage (); uninitialized_move (other.begin_ptr (), other.end_ptr (), data_ptr ()); } set_size (other.get_size ()); } } public: // small_vector_base (void) = impl; small_vector_base (const small_vector_base&) = delete; small_vector_base (small_vector_base&&) noexcept = delete; small_vector_base& operator= (const small_vector_base&) = delete; small_vector_base& operator= (small_vector_base&&) noexcept = delete; // ~small_vector_base (void) = impl; GCH_CPP20_CONSTEXPR small_vector_base (void) noexcept { set_default (); } static constexpr struct bypass_tag { } bypass { }; template <unsigned I, typename A> GCH_CPP20_CONSTEXPR small_vector_base (bypass_tag, const small_vector_base<Allocator, I>& other, const A& alloc) : alloc_interface (alloc) { if (InlineCapacity < other.get_size ()) { set_data_ptr (unchecked_allocate (other.get_size (), other.allocation_end_ptr ())); set_capacity (other.get_size ()); GCH_TRY { uninitialized_copy (other.begin_ptr (), other.end_ptr (), data_ptr ()); } GCH_CATCH (...) { deallocate (data_ptr (), get_capacity ()); GCH_THROW; } } else { set_to_inline_storage (); uninitialized_copy (other.begin_ptr (), other.end_ptr (), data_ptr ()); } set_size (other.get_size ()); } template <unsigned I> GCH_CPP20_CONSTEXPR small_vector_base (bypass_tag, const small_vector_base<Allocator, I>& other) : small_vector_base (bypass_tag {}, other, static_cast<const alloc_interface &> (other)) {} template <unsigned I> GCH_CPP20_CONSTEXPR small_vector_base (bypass_tag, small_vector_base<Allocator, I>&& other) noexcept (std::is_nothrow_move_constructible<value_ty>::value || (I == 0 && I == InlineCapacity)) : alloc_interface (std::move (other)) { move_initialize (std::move (other)); } template <unsigned I, typename A = alloc_ty, typename std::enable_if<std::is_same<std::allocator<value_ty>, A>::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<A>::is_always_equal::value #endif >::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base (bypass_tag, small_vector_base<Allocator, I>&& other, const alloc_ty&) noexcept (noexcept (small_vector_base (bypass, std::move (other)))) : small_vector_base (bypass, std::move (other)) { } template <unsigned I, typename A = alloc_ty, typename std::enable_if<! (std::is_same<std::allocator<value_ty>, A>::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<A>::is_always_equal::value #endif )>::type * = nullptr> GCH_CPP20_CONSTEXPR small_vector_base (bypass_tag, small_vector_base<Allocator, I>&& other, const alloc_ty& alloc) : alloc_interface (alloc) { if (other.allocator_ref () == alloc) { move_initialize (std::move (other)); return; } if (InlineCapacity < other.get_size ()) { // We may throw in this case. set_data_ptr (unchecked_allocate (other.get_size (), other.allocation_end_ptr ())); set_capacity (other.get_size ()); GCH_TRY { uninitialized_move (other.begin_ptr (), other.end_ptr (), data_ptr ()); } GCH_CATCH (...) { deallocate (data_ptr (), get_capacity ()); GCH_THROW; } } else { set_to_inline_storage (); uninitialized_move (other.begin_ptr (), other.end_ptr (), data_ptr ()); } set_size (other.get_size ()); } GCH_CPP20_CONSTEXPR explicit small_vector_base (const alloc_ty& alloc) noexcept : alloc_interface (alloc) { set_default (); } GCH_CPP20_CONSTEXPR small_vector_base (size_ty count, const alloc_ty& alloc) : alloc_interface (alloc) { if (InlineCapacity < count) { set_data_ptr (checked_allocate (count)); set_capacity (count); } else set_to_inline_storage (); GCH_TRY { uninitialized_value_construct (begin_ptr (), unchecked_next (begin_ptr (), count)); } GCH_CATCH (...) { if (has_allocation ()) deallocate (data_ptr (), get_capacity ()); GCH_THROW; } set_size (count); } GCH_CPP20_CONSTEXPR small_vector_base (size_ty count, const value_ty& val, const alloc_ty& alloc) : alloc_interface (alloc) { if (InlineCapacity < count) { set_data_ptr (checked_allocate (count)); set_capacity (count); } else set_to_inline_storage (); GCH_TRY { uninitialized_fill (begin_ptr (), unchecked_next (begin_ptr (), count), val); } GCH_CATCH (...) { if (has_allocation ()) deallocate (data_ptr (), get_capacity ()); GCH_THROW; } set_size (count); } template <typename Generator> GCH_CPP20_CONSTEXPR small_vector_base (size_ty count, Generator& g, const alloc_ty& alloc) : alloc_interface (alloc) { if (InlineCapacity < count) { set_data_ptr (checked_allocate (count)); set_capacity (count); } else set_to_inline_storage (); ptr curr = begin_ptr (); const ptr new_end = unchecked_next (begin_ptr (), count); GCH_TRY { for (; ! (curr == new_end); ++curr) construct (curr, g ()); } GCH_CATCH (...) { destroy_range (begin_ptr (), curr); if (has_allocation ()) deallocate (data_ptr (), get_capacity ()); GCH_THROW; } set_size (count); } #ifdef GCH_LIB_CONCEPTS template <std::input_iterator InputIt> #else template <typename InputIt> #endif GCH_CPP20_CONSTEXPR small_vector_base (InputIt first, InputIt last, std::input_iterator_tag, const alloc_ty& alloc) : small_vector_base (alloc) { using iterator_cat = typename std::iterator_traits<InputIt>::iterator_category; append_range (first, last, iterator_cat { }); } #ifdef GCH_LIB_CONCEPTS template <std::forward_iterator ForwardIt> #else template <typename ForwardIt> #endif GCH_CPP20_CONSTEXPR small_vector_base (ForwardIt first, ForwardIt last, std::forward_iterator_tag, const alloc_ty& alloc) : alloc_interface (alloc) { size_ty count = external_range_length (first, last); if (InlineCapacity < count) { set_data_ptr (unchecked_allocate (count)); set_capacity (count); GCH_TRY { uninitialized_copy (first, last, begin_ptr ()); } GCH_CATCH (...) { deallocate (data_ptr (), get_capacity ()); GCH_THROW; } } else { set_to_inline_storage (); uninitialized_copy (first, last, begin_ptr ()); } set_size (count); } GCH_CPP20_CONSTEXPR ~small_vector_base (void) noexcept { assert (InlineCapacity <= get_capacity () && "Invalid capacity."); wipe (); } protected: GCH_CPP20_CONSTEXPR void set_to_inline_storage (void) { set_capacity (InlineCapacity); #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) return set_data_ptr (alloc_interface::allocate (InlineCapacity)); #endif set_data_ptr (storage_ptr ()); } GCH_CPP20_CONSTEXPR void assign_with_copies (size_ty count, const value_ty& val) { if (get_capacity () < count) { size_ty new_capacity = checked_calculate_new_capacity (count); ptr new_begin = unchecked_allocate (new_capacity); GCH_TRY { uninitialized_fill (new_begin, unchecked_next (new_begin, count), val); } GCH_CATCH (...) { deallocate (new_begin, new_capacity); GCH_THROW; } reset_data (new_begin, new_capacity, count); } else if (get_size () < count) { std::fill (begin_ptr (), end_ptr (), val); uninitialized_fill (end_ptr (), unchecked_next (begin_ptr (), count), val); set_size (count); } else erase_range (std::fill_n (begin_ptr (), count, val), end_ptr ()); } template <typename InputIt, typename std::enable_if<std::is_assignable< value_ty&, decltype (*std::declval<InputIt> ())>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void assign_with_range (InputIt first, InputIt last, std::input_iterator_tag) { using iterator_cat = typename std::iterator_traits<InputIt>::iterator_category; ptr curr = begin_ptr (); for (; ! (end_ptr () == curr || first == last); ++curr, static_cast<void> (++first)) *curr = *first; if (first == last) erase_to_end (curr); else append_range (first, last, iterator_cat { }); } template <typename ForwardIt, typename std::enable_if<std::is_assignable< value_ty&, decltype (*std::declval<ForwardIt> ())>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void assign_with_range (ForwardIt first, ForwardIt last, std::forward_iterator_tag) { const size_ty count = external_range_length (first, last); if (get_capacity () < count) { size_ty new_capacity = checked_calculate_new_capacity (count); ptr new_begin = unchecked_allocate (new_capacity); GCH_TRY { uninitialized_copy (first, last, new_begin); } GCH_CATCH (...) { deallocate (new_begin, new_capacity); GCH_THROW; } reset_data (new_begin, new_capacity, count); } else if (get_size () < count) { ForwardIt pivot = copy_n_return_in (first, get_size (), begin_ptr ()); uninitialized_copy (pivot, last, end_ptr ()); set_size (count); } else erase_range (copy_range (first, last, begin_ptr ()), end_ptr ()); } template <typename InputIt, typename std::enable_if<! std::is_assignable< value_ty&, decltype (*std::declval<InputIt> ())>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void assign_with_range (InputIt first, InputIt last, std::input_iterator_tag) { using iterator_cat = typename std::iterator_traits<InputIt>::iterator_category; // If not assignable then destroy all elements and append. erase_all (); append_range (first, last, iterator_cat { }); } // Ie. move-if-noexcept. struct strong_exception_policy { }; template <typename Policy = void, typename V = value_ty, typename std::enable_if<is_explicitly_move_insertable<V>::value && (! std::is_same<Policy, strong_exception_policy>::value || relocate_with_move<V>::value), bool>::type = true> GCH_CPP20_CONSTEXPR ptr uninitialized_move (ptr first, ptr last, ptr d_first) noexcept (std::is_nothrow_move_constructible<value_ty>::value) { return uninitialized_copy (std::make_move_iterator (first), std::make_move_iterator (last), d_first); } template <typename Policy = void, typename V = value_ty, typename std::enable_if<! is_explicitly_move_insertable<V>::value || ( std::is_same<Policy, strong_exception_policy>::value &&! relocate_with_move<V>::value), bool>::type = false> GCH_CPP20_CONSTEXPR ptr uninitialized_move (ptr first, ptr last, ptr d_first) noexcept (alloc_interface::template is_uninitialized_memcpyable_iterator<ptr>::value) { return uninitialized_copy (first, last, d_first); } GCH_CPP20_CONSTEXPR ptr shift_into_uninitialized (ptr pos, size_ty n_shift) { // Shift elements over to the right into uninitialized space. // Returns the start of the shifted range. // Precondition: shift < end_ptr () - pos assert (n_shift != 0 && "The value of `n_shift` should not be 0."); const ptr original_end = end_ptr (); const ptr pivot = unchecked_prev (original_end, n_shift); uninitialized_move (pivot, original_end, original_end); increase_size (n_shift); return move_right (pos, pivot, original_end); } template <typename ...Args> GCH_CPP20_CONSTEXPR ptr append_element (Args&&... args) { if (get_size () < get_capacity ()) return emplace_into_current_end (std::forward<Args> (args)...); return emplace_into_reallocation_end (std::forward<Args> (args)...); } GCH_CPP20_CONSTEXPR ptr append_copies (size_ty count, const value_ty& val) { if (num_uninitialized () < count) { // Reallocate. if (get_max_size () - get_size () < count) throw_allocation_size_error (); size_ty original_size = get_size (); size_ty new_size = get_size () + count; // The check is handled by the if-guard. size_ty new_capacity = unchecked_calculate_new_capacity (new_size); ptr new_data_ptr = unchecked_allocate (new_capacity, allocation_end_ptr ()); ptr new_last = unchecked_next (new_data_ptr, original_size); GCH_TRY { new_last = uninitialized_fill (new_last, unchecked_next (new_last, count), val); uninitialized_move (begin_ptr (), end_ptr (), new_data_ptr); } GCH_CATCH (...) { destroy_range (unchecked_next (new_data_ptr, original_size), new_last); deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, new_size); return unchecked_next (new_data_ptr, original_size); } else { const ptr ret = end_ptr (); uninitialized_fill (ret, unchecked_next (ret, count), val); increase_size (count); return ret; } } template <typename MovePolicy, typename InputIt, typename std::enable_if< std::is_same<MovePolicy, strong_exception_policy>::value, bool>::type = true> GCH_CPP20_CONSTEXPR ptr append_range (InputIt first, InputIt last, std::input_iterator_tag) { // Append with a strong exception guarantee. size_ty original_size = get_size (); for (; ! (first == last); ++first) { GCH_TRY { append_element (*first); } GCH_CATCH (...) { erase_range (unchecked_next (begin_ptr (), original_size), end_ptr ()); GCH_THROW; } } return unchecked_next (begin_ptr (), original_size); } template <typename MovePolicy = void, typename InputIt, typename std::enable_if< ! std::is_same<MovePolicy, strong_exception_policy>::value, bool>::type = false> GCH_CPP20_CONSTEXPR ptr append_range (InputIt first, InputIt last, std::input_iterator_tag) { size_ty original_size = get_size (); for (; ! (first == last); ++first) append_element (*first); return unchecked_next (begin_ptr (), original_size); } template <typename MovePolicy = void, typename ForwardIt> GCH_CPP20_CONSTEXPR ptr append_range (ForwardIt first, ForwardIt last, std::forward_iterator_tag) { const size_ty num_insert = external_range_length (first, last); if (num_uninitialized () < num_insert) { // Reallocate. if (get_max_size () - get_size () < num_insert) throw_allocation_size_error (); size_ty original_size = get_size (); size_ty new_size = get_size () + num_insert; // The check is handled by the if-guard. size_ty new_capacity = unchecked_calculate_new_capacity (new_size); ptr new_data_ptr = unchecked_allocate (new_capacity, allocation_end_ptr ()); ptr new_last = unchecked_next (new_data_ptr, original_size); GCH_TRY { new_last = uninitialized_copy (first, last, new_last); uninitialized_move<MovePolicy> (begin_ptr (), end_ptr (), new_data_ptr); } GCH_CATCH (...) { destroy_range (unchecked_next (new_data_ptr, original_size), new_last); deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, new_size); return unchecked_next (new_data_ptr, original_size); } else { ptr ret = end_ptr (); uninitialized_copy (first, last, ret); increase_size (num_insert); return ret; } } template <typename ...Args> GCH_CPP20_CONSTEXPR ptr emplace_at (ptr pos, Args&&... args) { assert (get_size () <= get_capacity () && "size was greater than capacity"); if (get_size () < get_capacity ()) return emplace_into_current (pos, std::forward<Args> (args)...); return emplace_into_reallocation (pos, std::forward<Args> (args)...); } GCH_CPP20_CONSTEXPR ptr insert_copies (ptr pos, size_ty count, const value_ty& val) { if (0 == count) return pos; if (end_ptr () == pos) { if (1 == count) return append_element (val); return append_copies (count, val); } if (num_uninitialized () < count) { // Reallocate. if (get_max_size () - get_size () < count) throw_allocation_size_error (); const size_ty offset = internal_range_length (begin_ptr (), pos); const size_ty new_size = get_size () + count; // The check is handled by the if-guard. const size_ty new_capacity = unchecked_calculate_new_capacity (new_size); ptr new_data_ptr = unchecked_allocate (new_capacity, allocation_end_ptr ()); ptr new_first = unchecked_next (new_data_ptr, offset); ptr new_last = new_first; GCH_TRY { uninitialized_fill (new_first, unchecked_next (new_first, count), val); unchecked_advance (new_last, count); uninitialized_move (begin_ptr (), pos, new_data_ptr); new_first = new_data_ptr; uninitialized_move (pos, end_ptr (), new_last); } GCH_CATCH (...) { destroy_range (new_first, new_last); deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, new_size); return unchecked_next (begin_ptr (), offset); } else { // If we have fewer to insert than tailing elements after `pos`, we shift into // uninitialized and then copy over. const size_ty tail_size = internal_range_length (pos, end_ptr ()); if (tail_size < count) { // The number inserted is larger than the number after `pos`, // so part of the input will be used to construct new elements, // and another part of it will assign existing ones. // In order: // Construct new elements immediately after end_ptr () using the input. // Move-construct existing elements over to the tail. // Assign existing elements using the input. ptr original_end = end_ptr (); // Place a portion of the input into the uninitialized section. size_ty num_val_tail = count - tail_size; #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) { uninitialized_fill (end_ptr (), unchecked_next (end_ptr (), num_val_tail), val); increase_size (num_val_tail); const heap_temporary tmp (*this, val); uninitialized_move (pos, original_end, end_ptr ()); increase_size (tail_size); std::fill_n (pos, tail_size, tmp.get ()); return pos; } #endif uninitialized_fill (end_ptr (), unchecked_next (end_ptr (), num_val_tail), val); increase_size (num_val_tail); GCH_TRY { // We need to handle possible aliasing here. const stack_temporary tmp (*this, val); // Now, move the tail to the end. uninitialized_move (pos, original_end, end_ptr ()); increase_size (tail_size); GCH_TRY { // Finally, try to copy the rest of the elements over. std::fill_n (pos, tail_size, tmp.get ()); } GCH_CATCH (...) { // Attempt to roll back and destroy the tail if we fail. ptr inserted_end = unchecked_prev (end_ptr (), tail_size); move_left (inserted_end, end_ptr (), pos); destroy_range (inserted_end, end_ptr ()); decrease_size (tail_size); GCH_THROW; } } GCH_CATCH (...) { // Destroy the elements constructed from the input. destroy_range (original_end, end_ptr ()); decrease_size (internal_range_length (original_end, end_ptr ())); GCH_THROW; } } else { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) { const heap_temporary tmp (*this, val); ptr inserted_end = shift_into_uninitialized (pos, count); std::fill (pos, inserted_end, tmp.get ()); return pos; } #endif const stack_temporary tmp (*this, val); ptr inserted_end = shift_into_uninitialized (pos, count); // Attempt to copy over the elements. // If we fail we'll attempt a full roll-back. GCH_TRY { std::fill (pos, inserted_end, tmp.get ()); } GCH_CATCH (...) { ptr original_end = move_left (inserted_end, end_ptr (), pos); destroy_range (original_end, end_ptr ()); decrease_size (count); GCH_THROW; } } return pos; } } template <typename ForwardIt> GCH_CPP20_CONSTEXPR ptr insert_range_helper (ptr pos, ForwardIt first, ForwardIt last) { assert (! (first == last) && "The range should not be empty."); assert (! (end_ptr () == pos) && "`pos` should not be at the end."); const size_ty num_insert = external_range_length (first, last); if (num_uninitialized () < num_insert) { // Reallocate. if (get_max_size () - get_size () < num_insert) throw_allocation_size_error (); const size_ty offset = internal_range_length (begin_ptr (), pos); const size_ty new_size = get_size () + num_insert; // The check is handled by the if-guard. const size_ty new_capacity = unchecked_calculate_new_capacity (new_size); const ptr new_data_ptr = unchecked_allocate (new_capacity, allocation_end_ptr ()); ptr new_first = unchecked_next (new_data_ptr, offset); ptr new_last = new_first; GCH_TRY { uninitialized_copy (first, last, new_first); unchecked_advance (new_last, num_insert); uninitialized_move (begin_ptr (), pos, new_data_ptr); new_first = new_data_ptr; uninitialized_move (pos, end_ptr (), new_last); } GCH_CATCH (...) { destroy_range (new_first, new_last); deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, new_size); return unchecked_next (begin_ptr (), offset); } else { // if we have fewer to insert than tailing elements after // `pos` we shift into uninitialized and then copy over const size_ty tail_size = internal_range_length (pos, end_ptr ()); if (tail_size < num_insert) { // Use the same method as insert_copies. ptr original_end = end_ptr (); ForwardIt pivot = unchecked_next (first, tail_size); // Place a portion of the input into the uninitialized section. uninitialized_copy (pivot, last, end_ptr ()); increase_size (num_insert - tail_size); GCH_TRY { // Now move the tail to the end. uninitialized_move (pos, original_end, end_ptr ()); increase_size (tail_size); GCH_TRY { // Finally, try to copy the rest of the elements over. copy_range (first, pivot, pos); } GCH_CATCH (...) { // Attempt to roll back and destroy the tail if we fail. ptr inserted_end = unchecked_prev (end_ptr (), tail_size); move_left (inserted_end, end_ptr (), pos); destroy_range (inserted_end, end_ptr ()); decrease_size (tail_size); GCH_THROW; } } GCH_CATCH (...) { // If we throw, destroy the first copy we made. destroy_range (original_end, end_ptr ()); decrease_size (internal_range_length (original_end, end_ptr ())); GCH_THROW; } } else { shift_into_uninitialized (pos, num_insert); // Attempt to copy over the elements. // If we fail we'll attempt a full roll-back. GCH_TRY { copy_range (first, last, pos); } GCH_CATCH (...) { ptr inserted_end = unchecked_next (pos, num_insert); ptr original_end = move_left (inserted_end, end_ptr (), pos); destroy_range (original_end, end_ptr ()); decrease_size (num_insert); GCH_THROW; } } return pos; } } template <typename InputIt> GCH_CPP20_CONSTEXPR ptr insert_range (ptr pos, InputIt first, InputIt last, std::input_iterator_tag) { assert (! (first == last) && "The range should not be empty."); // Ensure we use this specific overload to give a strong exception guarantee for 1 element. if (end_ptr () == pos) return append_range (first, last, std::input_iterator_tag { }); using iterator_cat = typename std::iterator_traits<InputIt>::iterator_category; small_vector_base tmp (first, last, iterator_cat { }, allocator_ref ()); return insert_range_helper ( pos, std::make_move_iterator (tmp.begin_ptr ()), std::make_move_iterator (tmp.end_ptr ())); } template <typename ForwardIt> GCH_CPP20_CONSTEXPR ptr insert_range (ptr pos, ForwardIt first, ForwardIt last, std::forward_iterator_tag) { if (! (end_ptr () == pos)) return insert_range_helper (pos, first, last); if (unchecked_next (first) == last) return append_element (*first); using iterator_cat = typename std::iterator_traits<ForwardIt>::iterator_category; return append_range (first, last, iterator_cat { }); } template <typename ...Args> GCH_CPP20_CONSTEXPR ptr emplace_into_current_end (Args&&... args) { construct (end_ptr (), std::forward<Args> (args)...); increase_size (1); return unchecked_prev (end_ptr ()); } template <typename V = value_ty, typename std::enable_if< std::is_nothrow_move_constructible<V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR ptr emplace_into_current (ptr pos, value_ty&& val) { if (pos == end_ptr ()) return emplace_into_current_end (std::move (val)); // In the special case of value_ty&& we don't make a copy because behavior is unspecified // when it is an internal element. Hence, we'll take the opportunity to optimize and assume // that it isn't an internal element. shift_into_uninitialized (pos, 1); destroy (pos); construct (pos, std::move (val)); return pos; } template <typename ...Args> GCH_CPP20_CONSTEXPR ptr emplace_into_current (ptr pos, Args&&... args) { if (pos == end_ptr ()) return emplace_into_current_end (std::forward<Args> (args)...); #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) { heap_temporary tmp (*this, std::forward<Args> (args)...); shift_into_uninitialized (pos, 1); *pos = tmp.release (); return pos; } #endif // This is necessary because of possible aliasing. stack_temporary tmp (*this, std::forward<Args> (args)...); shift_into_uninitialized (pos, 1); *pos = tmp.release (); return pos; } template <typename ...Args> GCH_CPP20_CONSTEXPR ptr emplace_into_reallocation_end (Args&&... args) { // Appending; strong exception guarantee. if (get_max_size () == get_size ()) throw_allocation_size_error (); const size_ty new_size = get_size () + 1; // The check is handled by the if-guard. const size_ty new_capacity = unchecked_calculate_new_capacity (new_size); const ptr new_data_ptr = unchecked_allocate (new_capacity, allocation_end_ptr ()); const ptr emplace_pos = unchecked_next (new_data_ptr, get_size ()); GCH_TRY { construct (emplace_pos, std::forward<Args> (args)...); GCH_TRY { uninitialized_move<strong_exception_policy> (begin_ptr (), end_ptr (), new_data_ptr); } GCH_CATCH (...) { destroy (emplace_pos); GCH_THROW; } } GCH_CATCH (...) { deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, new_size); return emplace_pos; } template <typename ...Args> GCH_CPP20_CONSTEXPR ptr emplace_into_reallocation (ptr pos, Args&&... args) { const size_ty offset = internal_range_length (begin_ptr (), pos); if (offset == get_size ()) return emplace_into_reallocation_end (std::forward<Args> (args)...); if (get_max_size () == get_size ()) throw_allocation_size_error (); const size_ty new_size = get_size () + 1; // The check is handled by the if-guard. const size_ty new_capacity = unchecked_calculate_new_capacity (new_size); const ptr new_data_ptr = unchecked_allocate (new_capacity, allocation_end_ptr ()); ptr new_first = unchecked_next (new_data_ptr, offset); ptr new_last = new_first; GCH_TRY { construct (new_first, std::forward<Args> (args)...); unchecked_advance (new_last, 1); uninitialized_move (begin_ptr (), pos, new_data_ptr); new_first = new_data_ptr; uninitialized_move (pos, end_ptr (), new_last); } GCH_CATCH (...) { destroy_range (new_first, new_last); deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, new_size); return unchecked_next (begin_ptr (), offset); } GCH_CPP20_CONSTEXPR ptr shrink_to_size (void) { if (! has_allocation () || get_size () == get_capacity ()) return begin_ptr (); // The rest runs only if allocated. size_ty new_capacity; ptr new_data_ptr; if (InlineCapacity < get_size ()) { new_capacity = get_size (); new_data_ptr = unchecked_allocate (new_capacity, allocation_end_ptr ()); } else { // We move to inline storage. new_capacity = InlineCapacity; #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) new_data_ptr = alloc_interface::allocate (InlineCapacity); else new_data_ptr = storage_ptr (); #else new_data_ptr = storage_ptr (); #endif } uninitialized_move (begin_ptr (), end_ptr (), new_data_ptr); destroy_range (begin_ptr (), end_ptr ()); deallocate (data_ptr (), get_capacity ()); set_data_ptr (new_data_ptr); set_capacity (new_capacity); return begin_ptr (); } template <typename ...ValueT> GCH_CPP20_CONSTEXPR void resize_with (size_ty new_size, const ValueT&... val) { // ValueT... should either be value_ty or empty. if (new_size == 0) erase_all (); if (get_capacity () < new_size) { // Reallocate. if (get_max_size () < new_size) throw_allocation_size_error (); const size_ty original_size = get_size (); // The check is handled by the if-guard. const size_ty new_capacity = unchecked_calculate_new_capacity (new_size); ptr new_data_ptr = unchecked_allocate (new_capacity, allocation_end_ptr ()); ptr new_last = unchecked_next (new_data_ptr, original_size); GCH_TRY { new_last = uninitialized_fill ( new_last, unchecked_next (new_data_ptr, new_size), val...); // Strong exception guarantee. uninitialized_move<strong_exception_policy> (begin_ptr (), end_ptr (), new_data_ptr); } GCH_CATCH (...) { destroy_range (unchecked_next (new_data_ptr, original_size), new_last); deallocate (new_data_ptr, new_capacity); GCH_THROW; } reset_data (new_data_ptr, new_capacity, new_size); } else if (get_size () < new_size) { // Construct in the uninitialized section. uninitialized_fill (end_ptr (), unchecked_next (begin_ptr (), new_size), val...); set_size (new_size); } else erase_range (unchecked_next (begin_ptr (), new_size), end_ptr ()); // Do nothing if the count is the same as the current size. } GCH_CPP20_CONSTEXPR void request_capacity (size_ty request) { if (request <= get_capacity ()) return; size_ty new_capacity = checked_calculate_new_capacity (request); ptr new_begin = unchecked_allocate (new_capacity); GCH_TRY { uninitialized_move<strong_exception_policy> (begin_ptr (), end_ptr (), new_begin); } GCH_CATCH (...) { deallocate (new_begin, new_capacity); GCH_THROW; } wipe (); set_data_ptr (new_begin); set_capacity (new_capacity); } GCH_CPP20_CONSTEXPR ptr erase_at (ptr pos) { move_left (unchecked_next (pos), end_ptr (), pos); erase_last (); return pos; } GCH_CPP20_CONSTEXPR void erase_last (void) { decrease_size (1); // The element located at end_ptr is still alive since the size decreased. destroy (end_ptr ()); } GCH_CPP20_CONSTEXPR ptr erase_range (ptr first, ptr last) { if (! (first == last)) erase_to_end (move_left (last, end_ptr (), first)); return first; } GCH_CPP20_CONSTEXPR void erase_to_end (ptr pos) { assert (0 <= (end_ptr () - pos) && "`pos` was in the uninitialized range"); if (size_ty change = internal_range_length (pos, end_ptr ())) { decrease_size (change); destroy_range (pos, unchecked_next (pos, change)); } } GCH_CPP20_CONSTEXPR void erase_all (void) { ptr curr_end = end_ptr (); set_size (0); destroy_range (begin_ptr (), curr_end); } GCH_CPP20_CONSTEXPR void swap_elements (small_vector_base& other) noexcept (std::is_nothrow_move_constructible<value_ty>::value #ifdef GCH_LIB_IS_SWAPPABLE && std::is_nothrow_swappable<value_ty>::value #else && detail::small_vector_adl::is_nothrow_swappable<value_ty>::value #endif ) { assert (get_size () <= other.get_size ()); const ptr other_tail = std::swap_ranges (begin_ptr (), end_ptr (), other.begin_ptr ()); uninitialized_move (other_tail, other.end_ptr (), end_ptr ()); destroy_range (other_tail, other.end_ptr ()); swap_size (other); } GCH_CPP20_CONSTEXPR void swap_default (small_vector_base& other) noexcept (std::is_nothrow_move_constructible<value_ty>::value #ifdef GCH_LIB_IS_SWAPPABLE && std::is_nothrow_swappable<value_ty>::value #else && detail::small_vector_adl::is_nothrow_swappable<value_ty>::value #endif ) { // This function is used when: // We are using the standard allocator. // The allocators propagate and are equal. // The allocators are always equal. // The allocators do not propagate and are equal. // The allocators propagate and are not equal. // Not handled: // The allocators do not propagate and are not equal. assert (get_capacity () <= other.get_capacity ()); if (has_allocation ()) // Implies that `other` also has an allocation. swap_allocation (other); else if (other.has_allocation ()) { // Note: This will never be constant evaluated because both are always allocated. uninitialized_move (begin_ptr (), end_ptr (), other.storage_ptr ()); destroy_range (begin_ptr (), end_ptr ()); set_data_ptr (other.data_ptr ()); set_capacity (other.get_capacity ()); other.set_data_ptr (other.storage_ptr ()); other.set_capacity (InlineCapacity); swap_size (other); } else if (get_size () < other.get_size ()) swap_elements (other); else other.swap_elements (*this); alloc_interface::maybe_swap (other); } GCH_CPP20_CONSTEXPR void swap_unequal_no_propagate (small_vector_base& other) { assert (get_capacity () <= other.get_capacity ()); if (get_capacity () < other.get_size ()) { // Reallocation required. // We should always be able to reuse the allocation of `other`. const size_ty new_capacity = unchecked_calculate_new_capacity (other.get_size ()); const ptr new_data_ptr = unchecked_allocate (new_capacity, end_ptr ()); GCH_TRY { uninitialized_move (other.begin_ptr (), other.end_ptr (), new_data_ptr); GCH_TRY { destroy_range ( std::move (begin_ptr (), end_ptr (), other.begin_ptr ()), other.end_ptr ()); } GCH_CATCH (...) { destroy_range (new_data_ptr, unchecked_next (new_data_ptr, other.get_size ())); GCH_THROW; } } GCH_CATCH (...) { deallocate (new_data_ptr, new_capacity); GCH_THROW; } destroy_range (begin_ptr (), end_ptr ()); if (has_allocation ()) deallocate (data_ptr (), get_capacity ()); set_data_ptr (new_data_ptr); set_capacity (new_capacity); swap_size (other); } else if (get_size () < other.get_size ()) swap_elements (other); else other.swap_elements (*this); // This should have no effect. alloc_interface::maybe_swap (other); } template <typename A = alloc_ty, typename std::enable_if<allocations_are_swappable<A>::value && InlineCapacity == 0>::type * = nullptr> GCH_CPP20_CONSTEXPR void swap (small_vector_base& other) noexcept { swap_allocation (other); alloc_interface::maybe_swap (other); } template <typename A = alloc_ty, typename std::enable_if<allocations_are_swappable<A>::value && InlineCapacity != 0>::type * = nullptr> GCH_CPP20_CONSTEXPR void swap (small_vector_base& other) noexcept (std::is_nothrow_move_constructible<value_ty>::value #ifdef GCH_LIB_IS_SWAPPABLE && std::is_nothrow_swappable<value_ty>::value #else && detail::small_vector_adl::is_nothrow_swappable<value_ty>::value #endif ) { if (get_capacity () < other.get_capacity ()) swap_default (other); else other.swap_default (*this); } template <typename A = alloc_ty, typename std::enable_if<! allocations_are_swappable<A>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR void swap (small_vector_base& other) { if (get_capacity () < other.get_capacity ()) { if (other.allocator_ref () == allocator_ref ()) swap_default (other); else swap_unequal_no_propagate (other); } else { if (other.allocator_ref () == allocator_ref ()) other.swap_default (*this); else other.swap_unequal_no_propagate (*this); } } #ifdef __GLIBCXX__ // These are compatibility fixes for libstdc++ because std::copy doesn't work for // `move_iterator`s when constant evaluated. template <typename InputIt> static GCH_CPP20_CONSTEXPR InputIt unmove_iterator (InputIt it) { return it; } template <typename InputIt> static GCH_CPP20_CONSTEXPR auto unmove_iterator (std::move_iterator<InputIt> it) -> decltype (unmove_iterator (it.base ())) { return unmove_iterator (it.base ()); } template <typename InputIt> static GCH_CPP20_CONSTEXPR auto unmove_iterator (std::reverse_iterator<InputIt> it) -> std::reverse_iterator<decltype (unmove_iterator (it.base ()))> { return std::reverse_iterator<decltype (unmove_iterator (it.base ()))> ( unmove_iterator (it.base ())); } #endif template <typename InputIt> GCH_CPP20_CONSTEXPR ptr copy_range (InputIt first, InputIt last, ptr dest) { #if defined (GCH_LIB_IS_CONSTANT_EVALUATED) && defined (__GLIBCXX__) if GCH_IF_CONSTEXPR (! std::is_same<decltype (unmove_iterator (first)), InputIt>::value) if (std::is_constant_evaluated ()) return std::move (unmove_iterator (first), unmove_iterator (last), dest); #endif return std::copy (first, last, dest); } template <typename InputIt, typename std::enable_if< is_memcpyable_iterator<InputIt>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR InputIt copy_n_return_in (InputIt first, size_ty count, ptr dest) noexcept { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) { std::copy_n (first, count, dest); return unchecked_next (first, count); } #endif if (count != 0) std::memcpy (to_address (dest), to_address (first), count * sizeof (value_ty)); // Note: The unsafe cast here should be proven to be safe in the caller function. return unchecked_next (first, count); } template <typename InputIt, typename std::enable_if< is_memcpyable_iterator<InputIt>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR std::move_iterator<InputIt> copy_n_return_in (std::move_iterator<InputIt> first, size_ty count, ptr dest) noexcept { return std::move_iterator<InputIt> (copy_n_return_in (first.base (), count, dest)); } template <typename RandomIt, typename std::enable_if< ! is_memcpyable_iterator<RandomIt>::value && std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<RandomIt>::iterator_category>::value >::type * = nullptr> GCH_CPP20_CONSTEXPR RandomIt copy_n_return_in (RandomIt first, size_ty count, ptr dest) { #if defined (GCH_LIB_IS_CONSTANT_EVALUATED) && defined (__GLIBCXX__) if GCH_IF_CONSTEXPR (! std::is_same<decltype (unmove_iterator (first)), RandomIt>::value) { if (std::is_constant_evaluated ()) { auto bfirst = unmove_iterator (first); auto blast = unchecked_next (bfirst, count); std::move (bfirst, blast, dest); return unchecked_next (first, count); } } #endif std::copy_n (first, count, dest); // Note: This unsafe cast should be proven safe in the caller function. return unchecked_next (first, count); } template <typename InputIt, typename std::enable_if< ! is_memcpyable_iterator<InputIt>::value &&! std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<InputIt>::iterator_category>::value >::type * = nullptr> GCH_CPP20_CONSTEXPR InputIt copy_n_return_in (InputIt first, size_ty count, ptr dest) { for (; count != 0; --count, static_cast<void> (++dest), static_cast<void> (++first)) *dest = *first; return first; } template <typename V = value_ty, typename std::enable_if<is_memcpyable<V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR ptr move_left (ptr first, ptr last, ptr d_first) { // Shift initialized elements to the left. #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) return std::move (first, last, d_first); #endif const size_ty num_moved = internal_range_length (first, last); if (num_moved != 0) std::memmove (to_address (d_first), to_address (first), num_moved * sizeof (value_ty)); return unchecked_next (d_first, num_moved); } template <typename V = value_ty, typename std::enable_if<! is_memcpyable<V>::value>::type * = nullptr> GCH_CPP20_CONSTEXPR ptr move_left (ptr first, ptr last, ptr d_first) { // Shift initialized elements to the left. return std::move (first, last, d_first); } template <typename V = value_ty, typename std::enable_if<is_memcpyable<V>::value, bool>::type = true> GCH_CPP20_CONSTEXPR ptr move_right (ptr first, ptr last, ptr d_last) { // Move initialized elements to the right. #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) return std::move_backward (first, last, d_last); #endif const size_ty num_moved = internal_range_length (first, last); const ptr dest = unchecked_prev (d_last, num_moved); if (num_moved != 0) std::memmove (to_address (dest), to_address (first), num_moved * sizeof (value_ty)); return dest; } template <typename V = value_ty, typename std::enable_if<! is_memcpyable<V>::value, bool>::type = false> GCH_CPP20_CONSTEXPR ptr move_right (ptr first, ptr last, ptr d_last) { // move initialized elements to the right // n should not be 0 return std::move_backward (first, last, d_last); } public: GCH_CPP20_CONSTEXPR void set_default (void) { set_to_inline_storage (); set_size (0); } GCH_NODISCARD GCH_CPP14_CONSTEXPR ptr data_ptr (void) noexcept { return m_data.data_ptr (); } GCH_NODISCARD constexpr cptr data_ptr (void) const noexcept { return m_data.data_ptr (); } GCH_NODISCARD constexpr size_ty get_capacity (void) const noexcept { return m_data.capacity (); } GCH_NODISCARD constexpr size_ty get_size (void) const noexcept { return m_data.size (); } GCH_NODISCARD constexpr size_ty num_uninitialized (void) const noexcept { return get_capacity () - get_size (); } GCH_NODISCARD GCH_CPP14_CONSTEXPR ptr begin_ptr (void) noexcept { return data_ptr (); } GCH_NODISCARD constexpr cptr begin_ptr (void) const noexcept { return data_ptr (); } GCH_NODISCARD GCH_CPP14_CONSTEXPR ptr end_ptr (void) noexcept { return unchecked_next (begin_ptr (), get_size ()); } GCH_NODISCARD constexpr cptr end_ptr (void) const noexcept { return unchecked_next (begin_ptr (), get_size ()); } GCH_NODISCARD GCH_CPP14_CONSTEXPR ptr allocation_end_ptr (void) noexcept { return unchecked_next (begin_ptr (), get_capacity ()); } GCH_NODISCARD constexpr cptr allocation_end_ptr (void) const noexcept { return unchecked_next (begin_ptr (), get_capacity ()); } GCH_NODISCARD constexpr alloc_ty copy_allocator (void) const noexcept { return alloc_ty (allocator_ref ()); } GCH_NODISCARD GCH_CPP14_CONSTEXPR ptr storage_ptr (void) noexcept { return m_data.storage (); } GCH_NODISCARD constexpr bool has_allocation (void) const noexcept { #ifdef GCH_LIB_IS_CONSTANT_EVALUATED if (std::is_constant_evaluated ()) return true; #endif return InlineCapacity < get_capacity (); } GCH_NODISCARD constexpr bool is_inlinable (void) const noexcept { return get_size () <= InlineCapacity; } private: small_vector_data<ptr, size_type, value_ty, InlineCapacity> m_data; }; } // namespace gch::detail template <typename T, unsigned InlineCapacity, typename Allocator> #ifdef GCH_LIB_CONCEPTS requires concepts::small_vector::AllocatorFor<Allocator, T> #endif class small_vector : private detail::small_vector_base<Allocator, InlineCapacity> { using base = detail::small_vector_base<Allocator, InlineCapacity>; public: static_assert (std::is_same<T, typename Allocator::value_type>::value, "`Allocator::value_type` must be the same as `T`."); template <typename SameT, unsigned DifferentInlineCapacity, typename SameAllocator> #ifdef GCH_LIB_CONCEPTS requires concepts::small_vector::AllocatorFor<SameAllocator, SameT> #endif friend class small_vector; using value_type = T; using allocator_type = Allocator; using size_type = typename base::size_type; using difference_type = typename base::difference_type; using reference = value_type&; using const_reference = const value_type&; using pointer = typename std::allocator_traits<allocator_type>::pointer; using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer; using iterator = small_vector_iterator<pointer, difference_type>; using const_iterator = small_vector_iterator<const_pointer, difference_type>; using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; static_assert (InlineCapacity <= (std::numeric_limits<size_type>::max) (), "InlineCapacity must be less than or equal to the maximum value of size_type."); static constexpr unsigned inline_capacity_v = InlineCapacity; #ifdef GCH_LIB_CONCEPTS private: static constexpr bool Destructible = concepts::small_vector::Destructible<value_type>; static constexpr bool MoveAssignable = concepts::small_vector::MoveAssignable<value_type>; static constexpr bool CopyAssignable = concepts::small_vector::CopyAssignable<value_type>; static constexpr bool MoveConstructible = concepts::small_vector::MoveConstructible<value_type>; static constexpr bool CopyConstructible = concepts::small_vector::CopyConstructible<value_type>; static constexpr bool Swappable = concepts::small_vector::Swappable<value_type>; static constexpr bool DefaultInsertable = concepts::small_vector::DefaultInsertable<value_type, small_vector, allocator_type>; static constexpr bool MoveInsertable = concepts::small_vector::MoveInsertable<value_type, small_vector, allocator_type>; static constexpr bool CopyInsertable = concepts::small_vector::CopyInsertable<value_type, small_vector, allocator_type>; static constexpr bool Erasable = concepts::small_vector::Erasable<value_type, small_vector, allocator_type>; template <typename ...Args> struct EmplaceConstructible { static constexpr bool value = concepts::small_vector::EmplaceConstructible<value_type, small_vector, allocator_type, Args...>; }; public: #endif GCH_CPP20_CONSTEXPR small_vector (void) noexcept (noexcept (allocator_type ())) #ifdef GCH_LIB_CONCEPTS requires concepts::DefaultConstructible<allocator_type> #endif = default; GCH_CPP20_CONSTEXPR small_vector (const small_vector& other) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable #endif : base (base::bypass, other) { } GCH_CPP20_CONSTEXPR small_vector (small_vector&& other) noexcept (std::is_nothrow_move_constructible<value_type>::value || InlineCapacity == 0) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable #endif : base (base::bypass, std::move (other)) { } GCH_CPP20_CONSTEXPR explicit small_vector (const allocator_type& alloc) noexcept : base (alloc) { } GCH_CPP20_CONSTEXPR small_vector (const small_vector& other, const allocator_type& alloc) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable #endif : base (base::bypass, other, alloc) { } GCH_CPP20_CONSTEXPR small_vector (small_vector&& other, const allocator_type& alloc) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable #endif : base (base::bypass, std::move (other), alloc) { } GCH_CPP20_CONSTEXPR explicit small_vector (size_type count, const allocator_type& alloc = allocator_type ()) #ifdef GCH_LIB_CONCEPTS requires DefaultInsertable #endif : base (count, alloc) { } GCH_CPP20_CONSTEXPR small_vector (size_type count, const_reference value, const allocator_type& alloc = allocator_type ()) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable #endif : base (count, value, alloc) { } #ifdef GCH_LIB_CONCEPTS template <typename Generator> requires std::invocable<Generator&> && EmplaceConstructible<std::invoke_result_t<Generator&>>::value #else template <typename Generator, typename std::enable_if< ! std::is_convertible<Generator, const_reference>::value>::type * = nullptr> #endif GCH_CPP20_CONSTEXPR small_vector (size_type count, Generator g, const allocator_type& alloc = allocator_type ()) : base (count, g, alloc) { } #ifdef GCH_LIB_CONCEPTS template <std::input_iterator InputIt> requires EmplaceConstructible<std::iter_reference_t<InputIt>>::value && (std::forward_iterator<InputIt> || MoveInsertable) #else template <typename InputIt, typename std::enable_if< std::is_base_of< std::input_iterator_tag, typename std::iterator_traits<InputIt>::iterator_category>::value >::type * = nullptr> #endif GCH_CPP20_CONSTEXPR small_vector (InputIt first, InputIt last, const allocator_type& alloc = allocator_type ()) : base (first, last, typename std::iterator_traits<InputIt>::iterator_category { }, alloc) { } GCH_CPP20_CONSTEXPR small_vector (std::initializer_list<value_type> init, const allocator_type& alloc = allocator_type ()) #ifdef GCH_LIB_CONCEPTS requires EmplaceConstructible<const_reference>::value #endif : small_vector (init.begin (), init.end (), alloc) { } template <unsigned I> #ifdef GCH_LIB_CONCEPTS requires CopyInsertable #endif GCH_CPP20_CONSTEXPR explicit small_vector (const small_vector<T, I, Allocator>& other) : base (base::bypass, other) { } template <unsigned I> #ifdef GCH_LIB_CONCEPTS requires MoveInsertable #endif GCH_CPP20_CONSTEXPR explicit small_vector (small_vector<T, I, Allocator>&& other) noexcept (std::is_nothrow_move_constructible<value_type>::value && I < InlineCapacity) : base (base::bypass, std::move (other)) { } template <unsigned I> #ifdef GCH_LIB_CONCEPTS requires CopyInsertable #endif GCH_CPP20_CONSTEXPR small_vector (const small_vector<T, I, Allocator>& other, const allocator_type& alloc) : base (base::bypass, other, alloc) { } template <unsigned I> #ifdef GCH_LIB_CONCEPTS requires MoveInsertable #endif GCH_CPP20_CONSTEXPR small_vector (small_vector<T, I, Allocator>&& other, const allocator_type& alloc) : base (base::bypass, std::move (other), alloc) { } GCH_CPP20_CONSTEXPR ~small_vector (void) #ifdef GCH_LIB_CONCEPTS requires Erasable #endif = default; GCH_CPP20_CONSTEXPR small_vector& operator= (const small_vector& other) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable && CopyAssignable #endif { assign (other); return *this; } GCH_CPP20_CONSTEXPR small_vector& operator= (small_vector&& other) noexcept ( ( std::is_same<std::allocator<value_type>, Allocator>::value || std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<Allocator>::is_always_equal::value #endif ) && ( ( std::is_nothrow_move_assignable<value_type>::value && std::is_nothrow_move_constructible<value_type>::value ) || InlineCapacity == 0 ) ) #ifdef GCH_LIB_CONCEPTS // Note: The standard says here that // std::allocator_traits<allocator_type>::propagate_on_container_move_assignment == false // implies MoveInsertable && MoveAssignable, but since we have inline storage we must always // require moves [tab:container.alloc.req]. requires MoveInsertable && MoveAssignable #endif { assign (std::move (other)); return *this; } GCH_CPP20_CONSTEXPR small_vector& operator= (std::initializer_list<value_type> ilist) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable && CopyAssignable #endif { assign (ilist); return *this; } GCH_CPP20_CONSTEXPR void assign (size_type count, const_reference value) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable && CopyAssignable #endif { base::assign_with_copies (count, value); } #ifdef GCH_LIB_CONCEPTS template <std::input_iterator InputIt> requires EmplaceConstructible<std::iter_reference_t<InputIt>>::value && (std::forward_iterator<InputIt> || MoveInsertable) #else template <typename InputIt, typename std::enable_if<std::is_base_of< std::input_iterator_tag, typename std::iterator_traits<InputIt>::iterator_category >::value>::type * = nullptr> #endif GCH_CPP20_CONSTEXPR void assign (InputIt first, InputIt last) { using iterator_cat = typename std::iterator_traits<InputIt>::iterator_category; base::assign_with_range (first, last, iterator_cat { }); } GCH_CPP20_CONSTEXPR void assign (std::initializer_list<value_type> ilist) #ifdef GCH_LIB_CONCEPTS requires EmplaceConstructible<const_reference>::value #endif { assign (ilist.begin (), ilist.end ()); } GCH_CPP20_CONSTEXPR void assign (const small_vector& other) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable && CopyAssignable #endif { if (&other != this) base::copy_assign (other); } template <unsigned I> #ifdef GCH_LIB_CONCEPTS requires CopyInsertable && CopyAssignable #endif GCH_CPP20_CONSTEXPR void assign (const small_vector<T, I, Allocator>& other) { base::copy_assign (other); } GCH_CPP20_CONSTEXPR void assign (small_vector&& other) noexcept ( ( std::is_same<std::allocator<value_type>, Allocator>::value || std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<Allocator>::is_always_equal::value #endif ) && ( ( std::is_nothrow_move_assignable<value_type>::value && std::is_nothrow_move_constructible<value_type>::value ) || InlineCapacity == 0 ) ) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable && MoveAssignable #endif { if (&other != this) base::move_assign (std::move (other)); } template <unsigned I> #ifdef GCH_LIB_CONCEPTS requires MoveInsertable && MoveAssignable #endif GCH_CPP20_CONSTEXPR void assign (small_vector<T, I, Allocator>&& other) noexcept ( I <= InlineCapacity && ( std::is_same<std::allocator<value_type>, Allocator>::value || std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<Allocator>::is_always_equal::value #endif ) && std::is_nothrow_move_assignable<value_type>::value && std::is_nothrow_move_constructible<value_type>::value ) { base::move_assign (std::move (other)); } #ifndef GCH_LIB_CONCEPTS template <typename ValueType = value_type, typename std::enable_if< ( std::is_move_constructible<ValueType>::value && std::is_move_assignable<ValueType>::value #ifdef GCH_LIB_IS_SWAPPABLE && std::is_swappable<ValueType>::value #endif ) || ( ( std::is_same<std::allocator<value_type>, Allocator>::value || std::allocator_traits<Allocator>::propagate_on_container_swap::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<Allocator>::is_always_equal::value #endif ) && InlineCapacity == 0 ) >::type * = nullptr> #endif GCH_CPP20_CONSTEXPR void swap (small_vector& other) noexcept ( ( std::is_same<std::allocator<value_type>, Allocator>::value || std::allocator_traits<Allocator>::propagate_on_container_swap::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<Allocator>::is_always_equal::value #endif ) && ( ( std::is_nothrow_move_constructible<value_type>::value && std::is_nothrow_move_assignable<value_type>::value #ifdef GCH_LIB_IS_SWAPPABLE && std::is_nothrow_swappable<value_type>::value #else && detail::small_vector_adl::is_nothrow_swappable<value_type>::value #endif ) || InlineCapacity == 0 ) ) #ifdef GCH_LIB_CONCEPTS requires (MoveInsertable && MoveAssignable && Swappable) || ( ( std::is_same<std::allocator<value_type>, Allocator>::value || std::allocator_traits<Allocator>::propagate_on_container_swap::value #ifdef GCH_LIB_IS_ALWAYS_EQUAL || std::allocator_traits<Allocator>::is_always_equal::value #endif ) && InlineCapacity == 0 ) #endif { base::swap (other); } GCH_CPP14_CONSTEXPR iterator begin (void) noexcept { return iterator { base::begin_ptr () }; } constexpr const_iterator begin (void) const noexcept { return const_iterator { base::begin_ptr () }; } constexpr const_iterator cbegin (void) const noexcept { return begin (); } GCH_CPP14_CONSTEXPR iterator end (void) noexcept { return iterator { base::end_ptr () }; } constexpr const_iterator end (void) const noexcept { return const_iterator { base::end_ptr () }; } constexpr const_iterator cend (void) const noexcept { return end (); } GCH_CPP14_CONSTEXPR reverse_iterator rbegin (void) noexcept { return reverse_iterator { end () }; } constexpr const_reverse_iterator rbegin (void) const noexcept { return const_reverse_iterator { end () }; } constexpr const_reverse_iterator crbegin (void) const noexcept { return rbegin (); } GCH_CPP14_CONSTEXPR reverse_iterator rend (void) noexcept { return reverse_iterator { begin () }; } constexpr const_reverse_iterator rend (void) const noexcept { return const_reverse_iterator { begin () }; } constexpr const_reverse_iterator crend (void) const noexcept { return rend (); } GCH_CPP14_CONSTEXPR reference at (size_type pos) { if (size () <= pos) base::throw_index_error (); return begin ()[static_cast<difference_type> (pos)]; } GCH_CPP14_CONSTEXPR const_reference at (size_type pos) const { if (size () <= pos) base::throw_index_error (); return begin ()[static_cast<difference_type> (pos)]; } GCH_CPP14_CONSTEXPR reference operator[] (size_type pos) { return begin ()[static_cast<difference_type> (pos)]; } constexpr const_reference operator[] (size_type pos) const { return begin ()[static_cast<difference_type> (pos)]; } GCH_CPP14_CONSTEXPR reference front (void) { return (*this)[0]; } constexpr const_reference front (void) const { return (*this)[0]; } GCH_CPP14_CONSTEXPR reference back (void) { return (*this)[size () - 1]; } constexpr const_reference back (void) const { return (*this)[size () - 1]; } GCH_CPP14_CONSTEXPR pointer data (void) noexcept { return base::begin_ptr (); } constexpr const_pointer data (void) const noexcept { return base::begin_ptr (); } constexpr size_type size (void) const noexcept { return static_cast<size_type> (base::get_size ()); } GCH_NODISCARD constexpr bool empty (void) const noexcept { return size () == 0; } GCH_CPP14_CONSTEXPR size_type max_size (void) const noexcept { return static_cast<size_type> (base::get_max_size ()); } constexpr size_type capacity (void) const noexcept { return static_cast<size_type> (base::get_capacity ()); } constexpr allocator_type get_allocator (void) const noexcept { return base::copy_allocator (); } GCH_CPP20_CONSTEXPR iterator insert (const_iterator pos, const_reference value) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable && CopyAssignable #endif { return emplace (pos, value); } GCH_CPP20_CONSTEXPR iterator insert (const_iterator pos, value_type&& value) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable && MoveAssignable #endif { return emplace (pos, std::move (value)); } GCH_CPP20_CONSTEXPR iterator insert (const_iterator pos, size_type count, const_reference value) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable && CopyAssignable #endif { return iterator (base::insert_copies (base::ptr_cast (pos), count, value)); } // Note: Unlike std::vector, this does not require MoveConstructible because we // don't use std::rotate (as was the reason for the change in C++17). // Relevant: https://cplusplus.github.io/LWG/issue2266). #ifdef GCH_LIB_CONCEPTS template <std::input_iterator InputIt> requires EmplaceConstructible<std::iter_reference_t<InputIt>>::value && MoveInsertable && MoveAssignable #else template <typename InputIt, typename std::enable_if<std::is_base_of< std::input_iterator_tag, typename std::iterator_traits<InputIt>::iterator_category >::value>::type * = nullptr> #endif GCH_CPP20_CONSTEXPR iterator insert (const_iterator pos, InputIt first, InputIt last) { if (first == last) return iterator (base::ptr_cast (pos)); using iterator_cat = typename std::iterator_traits<InputIt>::iterator_category; return iterator (base::insert_range (base::ptr_cast (pos), first, last, iterator_cat { })); } GCH_CPP20_CONSTEXPR iterator insert (const_iterator pos, std::initializer_list<value_type> ilist) #ifdef GCH_LIB_CONCEPTS requires EmplaceConstructible<const_reference>::value && MoveInsertable && MoveAssignable #endif { return insert (pos, ilist.begin (), ilist.end ()); } template <typename ...Args> #ifdef GCH_LIB_CONCEPTS requires EmplaceConstructible<Args...>::value && MoveInsertable && MoveAssignable #endif GCH_CPP20_CONSTEXPR iterator emplace (const_iterator pos, Args&&... args) { return iterator (base::emplace_at (base::ptr_cast (pos), std::forward<Args> (args)...)); } GCH_CPP20_CONSTEXPR iterator erase (const_iterator pos) #ifdef GCH_LIB_CONCEPTS requires MoveAssignable && Erasable #endif { assert (0 <= (pos - begin ()) && "`pos` is out of bounds (before `begin ()`)." ); assert (0 < (end () - pos) && "`pos` is out of bounds (at or after `end ()`)."); return iterator (base::erase_at (base::ptr_cast (pos))); } GCH_CPP20_CONSTEXPR iterator erase (const_iterator first, const_iterator last) #ifdef GCH_LIB_CONCEPTS requires MoveAssignable && Erasable #endif { assert (0 <= (last - first) && "Invalid range."); assert (0 <= (first - begin ()) && "`first` is out of bounds (before `begin ()`)." ); assert (0 <= (end () - last) && "`last` is out of bounds (after `end ()`)."); return iterator (base::erase_range (base::ptr_cast (first), base::ptr_cast (last))); } GCH_CPP20_CONSTEXPR void push_back (const_reference value) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable #endif { emplace_back (value); } GCH_CPP20_CONSTEXPR void push_back (value_type&& value) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable #endif { emplace_back (std::move (value)); } template <typename ...Args> #ifdef GCH_LIB_CONCEPTS requires EmplaceConstructible<Args...>::value && MoveInsertable #endif GCH_CPP20_CONSTEXPR reference emplace_back (Args&&... args) { return *base::append_element (std::forward<Args> (args)...); } GCH_CPP20_CONSTEXPR void pop_back (void) #ifdef GCH_LIB_CONCEPTS requires Erasable #endif { assert (! empty () && "`pop_back ()` called on an empty `small_vector`."); base::erase_last (); } GCH_CPP20_CONSTEXPR void reserve (size_type new_capacity) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable #endif { base::request_capacity (new_capacity); } GCH_CPP20_CONSTEXPR void shrink_to_fit (void) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable #endif { base::shrink_to_size (); } GCH_CPP20_CONSTEXPR void clear (void) noexcept #ifdef GCH_LIB_CONCEPTS requires Erasable #endif { base::erase_all (); } GCH_CPP20_CONSTEXPR void resize (size_type count) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable && DefaultInsertable #endif { base::resize_with (count); } GCH_CPP20_CONSTEXPR void resize (size_type count, const_reference value) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable #endif { base::resize_with (count, value); } GCH_NODISCARD constexpr bool inlined (void) const noexcept { return ! base::has_allocation (); } GCH_NODISCARD constexpr bool inlinable (void) const noexcept { return base::is_inlinable (); } GCH_NODISCARD static GCH_CONSTEVAL size_type inline_capacity (void) noexcept { return static_cast<size_type> (inline_capacity_v); } #ifdef GCH_LIB_CONCEPTS template <std::input_iterator InputIt> requires EmplaceConstructible<std::iter_reference_t<InputIt>>::value && MoveInsertable #else template <typename InputIt, typename std::enable_if<std::is_base_of< std::input_iterator_tag, typename std::iterator_traits<InputIt>::iterator_category >::value>::type * = nullptr> #endif GCH_CPP20_CONSTEXPR small_vector& append (InputIt first, InputIt last) { using policy = typename base::strong_exception_policy; using iterator_cat = typename std::iterator_traits<InputIt>::iterator_category; base::template append_range<policy> (first, last, iterator_cat { }); return *this; } GCH_CPP20_CONSTEXPR small_vector& append (std::initializer_list<value_type> ilist) #ifdef GCH_LIB_CONCEPTS requires EmplaceConstructible<const_reference>::value && MoveInsertable #endif { return append (ilist.begin (), ilist.end ()); } template <unsigned I> GCH_CPP20_CONSTEXPR small_vector& append (const small_vector<T, I, Allocator>& other) #ifdef GCH_LIB_CONCEPTS requires CopyInsertable #endif { return append (other.begin (), other.end ()); } template <unsigned I> GCH_CPP20_CONSTEXPR small_vector& append (small_vector<T, I, Allocator>&& other) #ifdef GCH_LIB_CONCEPTS requires MoveInsertable #endif { // Provide a strong exception guarantee for `other` as well. using move_iter_type = typename std::conditional< base::template relocate_with_move<value_type>::value, std::move_iterator<iterator>, iterator>::type; append (move_iter_type { other.begin () }, move_iter_type { other.end () }); other.clear (); return *this; } }; template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator== (const small_vector<T, InlineCapacityLHS, Allocator>& lhs, const small_vector<T, InlineCapacityRHS, Allocator>& rhs) { return lhs.size () == rhs.size () && std::equal (lhs.begin (), lhs.end (), rhs.begin ()); } template <typename T, unsigned InlineCapacity, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator== (const small_vector<T, InlineCapacity, Allocator>& lhs, const small_vector<T, InlineCapacity, Allocator>& rhs) { return lhs.size () == rhs.size () && std::equal (lhs.begin (), lhs.end (), rhs.begin ()); } #ifdef GCH_LIB_THREE_WAY_COMPARISON template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS, typename Allocator> requires std::three_way_comparable<T> constexpr auto operator<=> (const small_vector<T, InlineCapacityLHS, Allocator>& lhs, const small_vector<T, InlineCapacityRHS, Allocator>& rhs) { return std::lexicographical_compare_three_way ( lhs.begin (), lhs.end (), rhs.begin (), rhs.end (), std::compare_three_way { }); } template <typename T, unsigned InlineCapacity, typename Allocator> requires std::three_way_comparable<T> constexpr auto operator<=> (const small_vector<T, InlineCapacity, Allocator>& lhs, const small_vector<T, InlineCapacity, Allocator>& rhs) { return std::lexicographical_compare_three_way ( lhs.begin (), lhs.end (), rhs.begin (), rhs.end (), std::compare_three_way { }); } template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS, typename Allocator> constexpr auto operator<=> (const small_vector<T, InlineCapacityLHS, Allocator>& lhs, const small_vector<T, InlineCapacityRHS, Allocator>& rhs) { constexpr auto comparison = [](const T& l, const T& r) { return (l < r) ? std::weak_ordering::less : (r < l) ? std::weak_ordering::greater : std::weak_ordering::equivalent; }; return std::lexicographical_compare_three_way ( lhs.begin (), lhs.end (), rhs.begin (), rhs.end (), comparison); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr auto operator<=> (const small_vector<T, InlineCapacity, Allocator>& lhs, const small_vector<T, InlineCapacity, Allocator>& rhs) { constexpr auto comparison = [](const T& l, const T& r) { return (l < r) ? std::weak_ordering::less : (r < l) ? std::weak_ordering::greater : std::weak_ordering::equivalent; }; return std::lexicographical_compare_three_way ( lhs.begin (), lhs.end (), rhs.begin (), rhs.end (), comparison); } #else template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator!= (const small_vector<T, InlineCapacityLHS, Allocator>& lhs, const small_vector<T, InlineCapacityRHS, Allocator>& rhs) { return ! (lhs == rhs); } template <typename T, unsigned InlineCapacity, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator!= (const small_vector<T, InlineCapacity, Allocator>& lhs, const small_vector<T, InlineCapacity, Allocator>& rhs) { return ! (lhs == rhs); } template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator< (const small_vector<T, InlineCapacityLHS, Allocator>& lhs, const small_vector<T, InlineCapacityRHS, Allocator>& rhs) { return std::lexicographical_compare (lhs.begin (), lhs.end (), rhs.begin (), rhs.end ()); } template <typename T, unsigned InlineCapacity, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator< (const small_vector<T, InlineCapacity, Allocator>& lhs, const small_vector<T, InlineCapacity, Allocator>& rhs) { return std::lexicographical_compare (lhs.begin (), lhs.end (), rhs.begin (), rhs.end ()); } template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator>= (const small_vector<T, InlineCapacityLHS, Allocator>& lhs, const small_vector<T, InlineCapacityRHS, Allocator>& rhs) { return ! (lhs < rhs); } template <typename T, unsigned InlineCapacity, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator>= (const small_vector<T, InlineCapacity, Allocator>& lhs, const small_vector<T, InlineCapacity, Allocator>& rhs) { return ! (lhs < rhs); } template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator> (const small_vector<T, InlineCapacityLHS, Allocator>& lhs, const small_vector<T, InlineCapacityRHS, Allocator>& rhs) { return rhs < lhs; } template <typename T, unsigned InlineCapacity, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator> (const small_vector<T, InlineCapacity, Allocator>& lhs, const small_vector<T, InlineCapacity, Allocator>& rhs) { return rhs < lhs; } template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator<= (const small_vector<T, InlineCapacityLHS, Allocator>& lhs, const small_vector<T, InlineCapacityRHS, Allocator>& rhs) { return rhs >= lhs; } template <typename T, unsigned InlineCapacity, typename Allocator> inline GCH_CPP20_CONSTEXPR bool operator<= (const small_vector<T, InlineCapacity, Allocator>& lhs, const small_vector<T, InlineCapacity, Allocator>& rhs) { return rhs >= lhs; } #endif template <typename T, unsigned InlineCapacity, typename Allocator #ifndef GCH_LIB_CONCEPTS , typename std::enable_if<std::is_move_constructible<T>::value && std::is_move_assignable<T>::value #ifdef GCH_LIB_IS_SWAPPABLE && std::is_swappable<T>::value #endif >::type * = nullptr #endif > inline GCH_CPP20_CONSTEXPR void swap (small_vector<T, InlineCapacity, Allocator>& lhs, small_vector<T, InlineCapacity, Allocator>& rhs) noexcept (noexcept (lhs.swap (rhs))) #ifdef GCH_LIB_CONCEPTS requires concepts::MoveInsertable<T, small_vector<T, InlineCapacity, Allocator>, Allocator> && concepts::Swappable<T> #endif { lhs.swap (rhs); } template <typename T, unsigned InlineCapacity, typename Allocator, typename U> inline GCH_CPP20_CONSTEXPR typename small_vector<T, InlineCapacity, Allocator>::size_type erase (small_vector<T, InlineCapacity, Allocator>& v, const U& value) { const auto original_size = v.size (); v.erase (std::remove (v.begin (), v.end (), value), v.end ()); return original_size - v.size (); } template <typename T, unsigned InlineCapacity, typename Allocator, typename Pred> inline GCH_CPP20_CONSTEXPR typename small_vector<T, InlineCapacity, Allocator>::size_type erase_if (small_vector<T, InlineCapacity, Allocator>& v, Pred pred) { const auto original_size = v.size (); v.erase (std::remove_if (v.begin (), v.end (), pred), v.end ()); return original_size - v.size (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::iterator begin (small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.begin (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_iterator begin (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.begin (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_iterator cbegin (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return begin (v); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::iterator end (small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.end (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_iterator end (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.end (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_iterator cend (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return end (v); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::reverse_iterator rbegin (small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.rbegin (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_reverse_iterator rbegin (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.rbegin (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_reverse_iterator crbegin (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return rbegin (v); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::reverse_iterator rend (small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.rend (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_reverse_iterator rend (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.rend (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_reverse_iterator crend (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return rend (v); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::size_type size (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.size (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename std::common_type< std::ptrdiff_t, typename std::make_signed< typename small_vector<T, InlineCapacity, Allocator>::size_type>::type>::type ssize (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { using ret_type = typename std::common_type< std::ptrdiff_t, typename std::make_signed<decltype (v.size ())>::type>::type; return static_cast<ret_type> (v.size ()); } template <typename T, unsigned InlineCapacity, typename Allocator> GCH_NODISCARD constexpr bool empty (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.empty (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::pointer data (small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.data (); } template <typename T, unsigned InlineCapacity, typename Allocator> constexpr typename small_vector<T, InlineCapacity, Allocator>::const_pointer data (const small_vector<T, InlineCapacity, Allocator>& v) noexcept { return v.data (); } #ifdef GCH_CTAD_SUPPORT template <typename InputIt, unsigned InlineCapacity = default_buffer_size< std::allocator<typename std::iterator_traits<InputIt>::value_type>>::value, typename Allocator = std::allocator<typename std::iterator_traits<InputIt>::value_type>> small_vector (InputIt, InputIt, Allocator = Allocator ()) -> small_vector<typename std::iterator_traits<InputIt>::value_type, InlineCapacity, Allocator>; #endif } // namespace gch #endif // GCH_SMALL_VECTOR_HPP