diff --git a/include/sg14/aa_inplace_vector.h b/include/sg14/aa_inplace_vector.h index d6a4920..9e40124 100644 --- a/include/sg14/aa_inplace_vector.h +++ b/include/sg14/aa_inplace_vector.h @@ -299,6 +299,9 @@ struct ipv_alloc_holder { ipv_alloc_holder& operator=(ipv_alloc_holder&&) noexcept = default; ~ipv_alloc_holder() noexcept = default; constexpr const Alloc& get_allocator_() const { return alloc_; } + constexpr void copy_allocator_(ipv_alloc_holder& rhs) { alloc_ = rhs.alloc_; } + constexpr void move_allocator_(ipv_alloc_holder& rhs) { alloc_ = std::move(rhs.alloc_); } + constexpr void swap_allocators_(ipv_alloc_holder& rhs) { using std::swap; swap(alloc_, rhs.alloc_); } }; template @@ -308,6 +311,9 @@ struct ipv_alloc_holder> { explicit ipv_alloc_holder() = default; constexpr explicit ipv_alloc_holder(const Alloc&) {} static constexpr Alloc get_allocator_() { return Alloc(); } + constexpr void copy_allocator_(ipv_alloc_holder&) { } + constexpr void move_allocator_(ipv_alloc_holder&) { } + static constexpr void swap_allocators_(ipv_alloc_holder&) { } }; template @@ -361,6 +367,9 @@ struct SG14_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF((sg14::aaipv::be_trivially_r using ipv_data_holder::size_type>::set_size_; using ipv_data_holder::size_type>::swap_sizes_; using ipv_alloc_holder::get_allocator_; + using ipv_alloc_holder::copy_allocator_; + using ipv_alloc_holder::move_allocator_; + using ipv_alloc_holder::swap_allocators_; // There is a feature-test macro for "conditionally trivial SMFs," namely // (__cpp_concepts >= 202002L); but in fact neither GCC 11.4 nor AppleClang 15 @@ -478,6 +487,8 @@ struct SG14_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF((sg14::aaipv::be_trivially_r { if constexpr (std::allocator_traits::propagate_on_container_copy_assignment::value && !std::allocator_traits::is_always_equal::value) { SG14_INPLACE_VECTOR_ASSERT_PRECONDITION(this->get_allocator_() == rhs.get_allocator_(), "operator= tried to propagate an unequal allocator; this is UB"); + // This line is redundant, since the allocators are supposed to be equal. + copy_allocator_(rhs); } if (this == std::addressof(rhs)) { // do nothing @@ -495,6 +506,8 @@ struct SG14_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF((sg14::aaipv::be_trivially_r { if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value && !std::allocator_traits::is_always_equal::value) { SG14_INPLACE_VECTOR_ASSERT_PRECONDITION(this->get_allocator_() == rhs.get_allocator_(), "operator= tried to propagate an unequal allocator; this is UB"); + // This line is redundant, since the allocators are supposed to be equal. + move_allocator_(rhs); } if (this == std::addressof(rhs)) { // do nothing @@ -536,21 +549,32 @@ struct SG14_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF((sg14::aaipv::be_trivially_r static constexpr void swap_(ipvbase& a, ipvbase& b) noexcept(SwapIsNoexcept) { - if constexpr (std::allocator_traits::propagate_on_container_swap::value && !std::allocator_traits::is_always_equal::value) { - SG14_INPLACE_VECTOR_ASSERT_PRECONDITION(a.get_allocator_() == b.get_allocator_(), "swap tried to swap unequal allocators; this is UB"); - } if (a.size_ < b.size_) { swap_(b, a); } else { + if constexpr (std::allocator_traits::propagate_on_container_swap::value && !std::allocator_traits::is_always_equal::value) { + SG14_INPLACE_VECTOR_ASSERT_PRECONDITION(a.get_allocator_() == b.get_allocator_(), "swap tried to swap unequal allocators; this is UB"); + // This line is redundant, since the allocators are supposed to be equal. + a.swap_allocators_(b); + } std::swap_ranges(a.data_, a.data_ + b.size_, b.data_); #if defined(__cpp_lib_trivially_relocatable) - size_t n = a.size_; - a.set_size_(b.size_); - sg14::aaipv::uninitialized_relocate_a(a.get_allocator_(), a.data_ + b.size_, a.data_ + n, b.data_ + b.size_); - b.set_size_(n); + if constexpr (std::is_trivially_relocatable_v && + sg14::aaipv::has_trivial_construct::value && + sg14::aaipv::has_trivial_destroy::value) { + size_t n = a.size_; + a.set_size_(b.size_); + std::uninitialized_relocate(a.data_ + b.size_, a.data_ + n, b.data_ + b.size_); + b.set_size_(n); + return; + } #else - sg14::aaipv::uninitialized_move_a(a.get_allocator_(), a.data_ + b.size_, a.data_ + a.size_, b.data_ + b.size_); - sg14::aaipv::destroy_a(a.get_allocator_(), a.data_ + b.size_, a.data_ + a.size_); + sg14::aaipv::uninitialized_move_a(b.get_allocator_(), a.data_ + b.size_, a.data_ + a.size_, b.data_ + b.size_); + if constexpr (std::allocator_traits::propagate_on_container_swap::value && !std::allocator_traits::is_always_equal::value) { + sg14::aaipv::destroy_a(b.get_allocator_(), a.data_ + b.size_, a.data_ + a.size_); + } else { + sg14::aaipv::destroy_a(a.get_allocator_(), a.data_ + b.size_, a.data_ + a.size_); + } a.swap_sizes_(b); #endif }