From b271e25fa94f3a7be55dca75f96a656361be2a14 Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Thu, 21 May 2026 13:17:27 +0200 Subject: [PATCH 01/10] Test correct alignment of SmallVector storage [why] We should verify that SmallVector actually places its elements according to their alignment requirements. As it turns out, it does. :) --- tests/test_SmallVector.cc | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/test_SmallVector.cc b/tests/test_SmallVector.cc index eddf602..44741e2 100644 --- a/tests/test_SmallVector.cc +++ b/tests/test_SmallVector.cc @@ -575,6 +575,68 @@ TEMPLATE_TEST_CASE("SmallVector: Iterator traits", "[SmallVector]", >::value == true); } +namespace { + +struct OverAligned +{ + alignas(1024) char data[64]; +}; + +template +std::pair get_front_and_back_pointers(VectorT& vec) +{ + if (vec.empty()) + throw std::logic_error("Vector is empty"); + + return { reinterpret_cast(&vec.front()), + reinterpret_cast(&vec.back()) }; +} + +template +void test_alignment_with() +{ + VectorT vec; + using ValueT = typename VectorT::ValueType; + + CAPTURE(vec.inner_capacity()); + CAPTURE(alignof(ValueT)); + + for (unsigned int i = 1u; i <= 50; ++i) + { + vec.push_back(ValueT{}); + CAPTURE(vec.size()); + + auto [front_ptr, back_ptr] = get_front_and_back_pointers(vec); + REQUIRE(front_ptr % alignof(ValueT) == 0); + REQUIRE(back_ptr % alignof(ValueT) == 0); + REQUIRE(back_ptr - front_ptr == sizeof(ValueT) * (vec.size() - 1u)); + } + + while (vec.size() > 1u) + { + vec.pop_back(); + vec.shrink_to_fit(); + CAPTURE(vec.size()); + + auto [front_ptr, back_ptr] = get_front_and_back_pointers(vec); + REQUIRE(front_ptr % alignof(ValueT) == 0); + REQUIRE(back_ptr % alignof(ValueT) == 0); + REQUIRE(back_ptr - front_ptr == sizeof(ValueT) * (vec.size() - 1u)); + } +} + +} // anonymous namespace + +TEMPLATE_TEST_CASE("SmallVector: Correct alignment after push_back() and shrink_to_fit()", + "[SmallVector]", char, short, double, std::string, OverAligned) +{ + test_alignment_with>(); + test_alignment_with>(); + test_alignment_with>(); + test_alignment_with>(); + test_alignment_with>(); +} + // // Tests for indiviual member functions (in alphabetical order) From 4276a122495624e938482cf2ce32a4c428ce4e40 Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Thu, 21 May 2026 13:23:55 +0200 Subject: [PATCH 02/10] Avoid usage of std::aligned_storage [why] std::aligned_storage is deprecated since C++23, see: https://stackoverflow.com/questions/71828288/why-is-stdaligned-storage-to-be-deprecated-in-c23-and-what-to-use-instead [how] Use alignas() for the internal buffer and specify the alignment explicitly when calling new for an allocated buffer. As it turns out, an aligned new[] has to be deallocated with an aligned delete[]. --- include/gul17/SmallVector.h | 87 +++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/include/gul17/SmallVector.h b/include/gul17/SmallVector.h index 337d3cc..1ea16e0 100644 --- a/include/gul17/SmallVector.h +++ b/include/gul17/SmallVector.h @@ -4,7 +4,7 @@ * \date Created on August 17, 2020 * \brief Definition of the SmallVector class template. * - * \copyright Copyright 2020-2023 Deutsches Elektronen-Synchrotron (DESY), Hamburg + * \copyright Copyright 2020-2026 Deutsches Elektronen-Synchrotron (DESY), Hamburg * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -25,16 +25,19 @@ #include #include +#include #include #include #include #include #include +#include #include #include -#include "gul17/internal.h" #include "gul17/cat.h" +#include "gul17/finalizer.h" +#include "gul17/internal.h" namespace gul17 { @@ -422,9 +425,7 @@ class SmallVector ~SmallVector() { clear(); - - if (is_storage_allocated()) - delete[] data_ptr_; + deallocate_storage(); } /** @@ -939,8 +940,7 @@ class SmallVector if (&other != this) { clear(); - if (is_storage_allocated()) - delete[] data_ptr_; + deallocate_storage(); move_or_copy_all_elements_from(std::move(other)); } @@ -1075,17 +1075,17 @@ class SmallVector if (new_capacity <= capacity_) return; - auto new_data = std::make_unique(new_capacity); + // Allocate aligned memory for the new "outer" capacity. + auto* new_data = new (alignment) std::byte[new_capacity * sizeof(ValueType)]; + auto _ = finally([&new_data]() { ::operator delete[](new_data, alignment); }); - const auto d_end = data_end(); - - uninitialized_move_or_copy(data(), d_end, reinterpret_cast(new_data.get())); + auto* d_end = data_end(); + uninitialized_move_or_copy(data(), d_end, reinterpret_cast(new_data)); destroy_range(data(), d_end); - if (is_storage_allocated()) - delete[] data_ptr_; - - data_ptr_ = new_data.release(); + deallocate_storage(); + data_ptr_ = new_data; + new_data = nullptr; capacity_ = new_capacity; } @@ -1161,25 +1161,34 @@ class SmallVector if (new_capacity == capacity_) return; - AlignedStorage* new_data; - auto new_memory = std::unique_ptr{}; + std::byte* new_data{}; + std::byte* allocation{}; + auto _ = finally([&allocation]() { ::operator delete[](allocation, alignment); }); - if (new_capacity == inner_capacity()) { + if (new_capacity == inner_capacity()) + { new_data = internal_array_.data(); - } else { - new_memory = std::make_unique(new_capacity); - new_data = new_memory.get(); + } + else + { + allocation = new (alignment) std::byte[new_capacity * sizeof(ValueType)]; + new_data = allocation; } - const auto d_end = data_end(); - + auto* d_end = data_end(); uninitialized_move_or_copy(data(), d_end, reinterpret_cast(new_data)); destroy_range(data(), d_end); - if (is_storage_allocated()) - delete[] data_ptr_; - - data_ptr_ = new_memory ? new_memory.release() : new_data; + deallocate_storage(); + if (allocation) + { + data_ptr_ = allocation; + allocation = nullptr; + } + else + { + data_ptr_ = new_data; + } capacity_ = new_capacity; } @@ -1212,17 +1221,17 @@ class SmallVector } private: - using AlignedStorage = - typename std::aligned_storage::type; + static constexpr std::align_val_t alignment{ alignof(ValueType) }; /** - * This union either holds the storage for the "internal" elements or, if the vector - * has grown beyond that size, a pointer to storage on the heap. + * Uninitialized storage for the internal elements (used only if size() <= + * inner_capacity()). */ - std::array internal_array_; + alignas(ValueType) + std::array internal_array_; /// Pointer to internal or external contiguous data storage. - AlignedStorage* data_ptr_{ internal_array_.data() }; + std::byte* data_ptr_{ internal_array_.data() }; /// Capacity of the vector (i.e. number of elements that can be stored without /// enlarging the container) @@ -1292,6 +1301,18 @@ class SmallVector return reinterpret_cast(data_ptr_) + size_; } + /** + * Deallocate the data buffer if it is allocated on the heap. + * If that was the case, data_ptr_ dangles after this call. + */ + void deallocate_storage() noexcept + { + if (!is_storage_allocated()) + return; + + ::operator delete[](data_ptr_, alignment); + } + /** * Call the destructor on all elements in the given range, turning the occupied space * into uninitialized memory. From 1477f3963ea88002343c52fae9f590034d117a3c Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Thu, 21 May 2026 15:31:03 +0200 Subject: [PATCH 03/10] Update changelog with standard library related changes --- data/doxygen.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/data/doxygen.h b/data/doxygen.h index 2f19ed4..f8028f7 100644 --- a/data/doxygen.h +++ b/data/doxygen.h @@ -4,7 +4,7 @@ * \date Created on August 24, 2018 * \brief Doxygen input file for the General Utility Library. * - * \copyright Copyright 2018-2025 Deutsches Elektronen-Synchrotron (DESY), Hamburg + * \copyright Copyright 2018-2026 Deutsches Elektronen-Synchrotron (DESY), Hamburg * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -146,6 +146,11 @@ namespace gul17 { * * \section changelog_gul17 GUL17 Versions * + * \subsection V26_X_X Unreleased + * + * - Avoid using std::result_of_t which is removed in C++20. + * - Avoid using std::aligned_storage which is deprecated in C++23. + * * \subsection V26_2_0 Version 26.2.0 * * - Add gul17::null_safe_string_view(const char*, std::size_t), which returns an empty From 395cbffbede9053a57cd5e50fa1ec5a12b5cee26 Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Fri, 22 May 2026 09:20:20 +0200 Subject: [PATCH 04/10] Set data_ptr_ to null in deallocate_storage() [why] The function could leave data_ptr_ dangling after the call. This was clearly documented and logically OK within the class, but could raise suspicion. [how] Explicitly document that the function may leave the class in a state in which the class invariants are violated. If deallocation takes place, assign nullptr to data_ptr_ so that there is a better chance to find misuses. --- include/gul17/SmallVector.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/gul17/SmallVector.h b/include/gul17/SmallVector.h index 1ea16e0..765b2ca 100644 --- a/include/gul17/SmallVector.h +++ b/include/gul17/SmallVector.h @@ -1303,7 +1303,8 @@ class SmallVector /** * Deallocate the data buffer if it is allocated on the heap. - * If that was the case, data_ptr_ dangles after this call. + * If that was the case, data_ptr_ is null after this call and must be reassigned to + * re-establish the class invariants. */ void deallocate_storage() noexcept { @@ -1311,6 +1312,7 @@ class SmallVector return; ::operator delete[](data_ptr_, alignment); + data_ptr_ = nullptr; } /** @@ -1509,6 +1511,8 @@ class SmallVector * performs no check for self-assignment. * * \pre `size() == 0 and is_allocated() == false and &other != this` + * \post `data_ptr_ != nullptr` (it either points to the allocated storage stolen from + * `other` or to the internal array) */ void move_or_copy_all_elements_from(SmallVector&& other) noexcept(std::is_nothrow_move_constructible::value) From 5117f00a9c973122c29b453f01d1ff5418cbc1fd Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Fri, 22 May 2026 10:13:57 +0200 Subject: [PATCH 05/10] Use low-level ::operator new[] instead of new (alignment) [why] MSVC mistakes the statement allocation = new (alignment) std::byte[new_capacity * sizeof(ValueType)]; for a placement new even if alignment is of the type std::align_val_t, and therefore generates a compilation error. [how] Use the low-level form of new directly like this: auto* ptr = static_cast( ::operator new[](num_elements * sizeof(ValueType), alignment)); Because this is ugly, extract both allocation and deallocation into static member functions. --- include/gul17/SmallVector.h | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/include/gul17/SmallVector.h b/include/gul17/SmallVector.h index 765b2ca..3b3e090 100644 --- a/include/gul17/SmallVector.h +++ b/include/gul17/SmallVector.h @@ -1076,8 +1076,8 @@ class SmallVector return; // Allocate aligned memory for the new "outer" capacity. - auto* new_data = new (alignment) std::byte[new_capacity * sizeof(ValueType)]; - auto _ = finally([&new_data]() { ::operator delete[](new_data, alignment); }); + auto* new_data = allocate_space_for_elements(new_capacity); + auto _ = finally([&new_data]() { deallocate_space_for_elements(new_data); }); auto* d_end = data_end(); uninitialized_move_or_copy(data(), d_end, reinterpret_cast(new_data)); @@ -1163,7 +1163,7 @@ class SmallVector std::byte* new_data{}; std::byte* allocation{}; - auto _ = finally([&allocation]() { ::operator delete[](allocation, alignment); }); + auto _ = finally([&allocation]() { deallocate_space_for_elements(allocation); }); if (new_capacity == inner_capacity()) { @@ -1171,7 +1171,7 @@ class SmallVector } else { - allocation = new (alignment) std::byte[new_capacity * sizeof(ValueType)]; + allocation = allocate_space_for_elements(new_capacity); new_data = allocation; } @@ -1239,6 +1239,16 @@ class SmallVector /// Number of elements stored in the container. SizeType size_{ 0u }; + /** + * Allocate aligned, uninitialized memory for storing a certain number of elements. + * The memory has to be deallocated with deallocate_space_for_elements() after use. + */ + static std::byte* allocate_space_for_elements(std::size_t num_elements) + { + return static_cast( + ::operator new[](num_elements * sizeof(ValueType), alignment)); + } + /** * Copy a range of values into uninitialized cells. * @@ -1301,6 +1311,12 @@ class SmallVector return reinterpret_cast(data_ptr_) + size_; } + /// Deallocate aligned memory that was reserved with allocate_space_for_elements(). + static void deallocate_space_for_elements(std::byte* ptr) noexcept + { + ::operator delete[](ptr, alignment); + } + /** * Deallocate the data buffer if it is allocated on the heap. * If that was the case, data_ptr_ is null after this call and must be reassigned to @@ -1311,7 +1327,7 @@ class SmallVector if (!is_storage_allocated()) return; - ::operator delete[](data_ptr_, alignment); + deallocate_space_for_elements(data_ptr_); data_ptr_ = nullptr; } From dcac1e4d321c83f0b1ec3ea7b6a75d999dc433d3 Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Fri, 22 May 2026 10:21:05 +0200 Subject: [PATCH 06/10] Inline deallocate_storage() [why] Having a private member function that breaks the class invariants may be confusing, and it does not make things any clearer than having the two lines directly in the code. --- include/gul17/SmallVector.h | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/include/gul17/SmallVector.h b/include/gul17/SmallVector.h index 3b3e090..a0967bf 100644 --- a/include/gul17/SmallVector.h +++ b/include/gul17/SmallVector.h @@ -425,7 +425,8 @@ class SmallVector ~SmallVector() { clear(); - deallocate_storage(); + if (is_storage_allocated()) + deallocate_space_for_elements(data_ptr_); } /** @@ -940,7 +941,8 @@ class SmallVector if (&other != this) { clear(); - deallocate_storage(); + if (is_storage_allocated()) + deallocate_space_for_elements(data_ptr_); move_or_copy_all_elements_from(std::move(other)); } @@ -1083,7 +1085,9 @@ class SmallVector uninitialized_move_or_copy(data(), d_end, reinterpret_cast(new_data)); destroy_range(data(), d_end); - deallocate_storage(); + if (is_storage_allocated()) + deallocate_space_for_elements(data_ptr_); + data_ptr_ = new_data; new_data = nullptr; capacity_ = new_capacity; @@ -1179,7 +1183,9 @@ class SmallVector uninitialized_move_or_copy(data(), d_end, reinterpret_cast(new_data)); destroy_range(data(), d_end); - deallocate_storage(); + if (is_storage_allocated()) + deallocate_space_for_elements(data_ptr_); + if (allocation) { data_ptr_ = allocation; @@ -1317,20 +1323,6 @@ class SmallVector ::operator delete[](ptr, alignment); } - /** - * Deallocate the data buffer if it is allocated on the heap. - * If that was the case, data_ptr_ is null after this call and must be reassigned to - * re-establish the class invariants. - */ - void deallocate_storage() noexcept - { - if (!is_storage_allocated()) - return; - - deallocate_space_for_elements(data_ptr_); - data_ptr_ = nullptr; - } - /** * Call the destructor on all elements in the given range, turning the occupied space * into uninitialized memory. From 7c0ab98baf1a1bb64b5c23df56fed5177d22f7d5 Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Fri, 22 May 2026 10:35:16 +0200 Subject: [PATCH 07/10] SmallVector: Simplify shrink_to_fit() --- include/gul17/SmallVector.h | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/include/gul17/SmallVector.h b/include/gul17/SmallVector.h index a0967bf..c5d2fcd 100644 --- a/include/gul17/SmallVector.h +++ b/include/gul17/SmallVector.h @@ -1186,16 +1186,9 @@ class SmallVector if (is_storage_allocated()) deallocate_space_for_elements(data_ptr_); - if (allocation) - { - data_ptr_ = allocation; - allocation = nullptr; - } - else - { - data_ptr_ = new_data; - } + data_ptr_ = new_data; capacity_ = new_capacity; + allocation = nullptr; // Avoid deallocation by the "finally" guard } /// Return the number of elements that are currently stored. From 6501d7fff666c68af72dbad7c5e84731a36563f4 Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Tue, 26 May 2026 15:49:50 +0200 Subject: [PATCH 08/10] Remove unnecessary "alignment" member [why] The code actually becomes more readable by using the expressions alignof(ValueType) or std::align_val_t{ alignof(ValueType) } directly. --- include/gul17/SmallVector.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/gul17/SmallVector.h b/include/gul17/SmallVector.h index c5d2fcd..51aa053 100644 --- a/include/gul17/SmallVector.h +++ b/include/gul17/SmallVector.h @@ -1220,8 +1220,6 @@ class SmallVector } private: - static constexpr std::align_val_t alignment{ alignof(ValueType) }; - /** * Uninitialized storage for the internal elements (used only if size() <= * inner_capacity()). @@ -1245,7 +1243,8 @@ class SmallVector static std::byte* allocate_space_for_elements(std::size_t num_elements) { return static_cast( - ::operator new[](num_elements * sizeof(ValueType), alignment)); + ::operator new[](num_elements * sizeof(ValueType), + std::align_val_t{ alignof(ValueType) })); } /** @@ -1313,7 +1312,7 @@ class SmallVector /// Deallocate aligned memory that was reserved with allocate_space_for_elements(). static void deallocate_space_for_elements(std::byte* ptr) noexcept { - ::operator delete[](ptr, alignment); + ::operator delete[](ptr, std::align_val_t{ alignof(ValueType) }); } /** From a3f3662b97cf922e810797128823ca772a692ece Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Tue, 26 May 2026 16:14:52 +0200 Subject: [PATCH 09/10] SmallVector: Remove most reinterpret_casts [how] If we store data_ptr as a ValueType* instead of std::byte* and access the internal array only via an accessor function get_internal_array_pointer(), we can limit the need for reinterpret_cast to that accessor function (the internal array is still an array of std::byte). Proposed-by: Fini Jastrow --- include/gul17/SmallVector.h | 62 +++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/include/gul17/SmallVector.h b/include/gul17/SmallVector.h index 51aa053..5ddde34 100644 --- a/include/gul17/SmallVector.h +++ b/include/gul17/SmallVector.h @@ -603,7 +603,7 @@ class SmallVector */ constexpr ValueType* data() noexcept { - return reinterpret_cast(data_ptr_); + return data_ptr_; } /** @@ -612,7 +612,7 @@ class SmallVector */ constexpr const ValueType* data() const noexcept { - return reinterpret_cast(data_ptr_); + return data_ptr_; } /** @@ -1082,7 +1082,7 @@ class SmallVector auto _ = finally([&new_data]() { deallocate_space_for_elements(new_data); }); auto* d_end = data_end(); - uninitialized_move_or_copy(data(), d_end, reinterpret_cast(new_data)); + uninitialized_move_or_copy(data(), d_end, new_data); destroy_range(data(), d_end); if (is_storage_allocated()) @@ -1165,13 +1165,13 @@ class SmallVector if (new_capacity == capacity_) return; - std::byte* new_data{}; - std::byte* allocation{}; + ValueType* new_data{}; + ValueType* allocation{}; auto _ = finally([&allocation]() { deallocate_space_for_elements(allocation); }); if (new_capacity == inner_capacity()) { - new_data = internal_array_.data(); + new_data = get_internal_array_pointer(); } else { @@ -1180,7 +1180,7 @@ class SmallVector } auto* d_end = data_end(); - uninitialized_move_or_copy(data(), d_end, reinterpret_cast(new_data)); + uninitialized_move_or_copy(data(), d_end, new_data); destroy_range(data(), d_end); if (is_storage_allocated()) @@ -1228,7 +1228,7 @@ class SmallVector std::array internal_array_; /// Pointer to internal or external contiguous data storage. - std::byte* data_ptr_{ internal_array_.data() }; + ValueType* data_ptr_{ get_internal_array_pointer() }; /// Capacity of the vector (i.e. number of elements that can be stored without /// enlarging the container) @@ -1240,9 +1240,9 @@ class SmallVector * Allocate aligned, uninitialized memory for storing a certain number of elements. * The memory has to be deallocated with deallocate_space_for_elements() after use. */ - static std::byte* allocate_space_for_elements(std::size_t num_elements) + static ValueType* allocate_space_for_elements(std::size_t num_elements) { - return static_cast( + return static_cast( ::operator new[](num_elements * sizeof(ValueType), std::align_val_t{ alignof(ValueType) })); } @@ -1297,20 +1297,20 @@ class SmallVector ::new(static_cast(p)) ValueType(value); } - /// Return a non-dereferencable pointer past the last element. + /// Return a non-dereferenceable pointer past the last element. constexpr ValueType* data_end() noexcept { - return reinterpret_cast(data_ptr_) + size_; + return data_ptr_ + size_; } - /// Return a non-dereferencable pointer past the last element. + /// Return a non-dereferenceable pointer past the last element. constexpr const ValueType* data_end() const noexcept { - return reinterpret_cast(data_ptr_) + size_; + return data_ptr_ + size_; } /// Deallocate aligned memory that was reserved with allocate_space_for_elements(). - static void deallocate_space_for_elements(std::byte* ptr) noexcept + static void deallocate_space_for_elements(ValueType* ptr) noexcept { ::operator delete[](ptr, std::align_val_t{ alignof(ValueType) }); } @@ -1395,6 +1395,18 @@ class SmallVector } } + /// Return a ValueType pointer to the internal array storage. + const ValueType* get_internal_array_pointer() const noexcept + { + return reinterpret_cast(internal_array_.data()); + } + + /// Return a ValueType pointer to the internal array storage. + ValueType* get_internal_array_pointer() noexcept + { + return reinterpret_cast(internal_array_.data()); + } + /** * Increase the capacity of the vector, typically by ~50%. * @@ -1453,7 +1465,7 @@ class SmallVector /// Determine if this vector is using allocated external storage. bool is_storage_allocated() const noexcept { - return data_ptr_ != internal_array_.data(); + return data_ptr_ != get_internal_array_pointer(); } /** @@ -1520,13 +1532,18 @@ class SmallVector // Can we simply steal the complete allocated storage? if (other.is_storage_allocated()) { - data_ptr_ = other.data_ptr_; other.data_ptr_ = other.internal_array_.data(); - capacity_ = other.capacity_; other.capacity_ = other.inner_capacity(); - size_ = other.size_; other.size_ = 0u; + data_ptr_ = other.data_ptr_; + other.data_ptr_ = other.get_internal_array_pointer(); + + capacity_ = other.capacity_; + other.capacity_ = other.inner_capacity(); + + size_ = other.size_; + other.size_ = 0u; } else // otherwise fall back to moving (or at least copying) all elements { - data_ptr_ = internal_array_.data(); + data_ptr_ = get_internal_array_pointer(); capacity_ = in_capacity; uninitialized_move_or_copy(other.begin(), other.end(), data()); size_ = other.size(); @@ -1554,12 +1571,11 @@ class SmallVector */ static void swap_heap_with_internal(SmallVector &a, SmallVector &b) { - uninitialized_move_or_copy(b.begin(), b.end(), - reinterpret_cast(a.internal_array_.data())); + uninitialized_move_or_copy(b.begin(), b.end(), a.get_internal_array_pointer()); destroy_range(b.begin(), b.end()); b.data_ptr_ = a.data_ptr_; - a.data_ptr_ = a.internal_array_.data(); + a.data_ptr_ = a.get_internal_array_pointer(); std::swap(a.capacity_, b.capacity_); std::swap(a.size_, b.size_); From 6ecb9593f399669a023da10efd2abffaee40ffa9 Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Tue, 26 May 2026 16:52:18 +0200 Subject: [PATCH 10/10] Use std::exchange where possible [why] It makes the code more compact and more readable. --- include/gul17/SmallVector.h | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/include/gul17/SmallVector.h b/include/gul17/SmallVector.h index 5ddde34..030ee99 100644 --- a/include/gul17/SmallVector.h +++ b/include/gul17/SmallVector.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "gul17/cat.h" #include "gul17/finalizer.h" @@ -1532,14 +1533,9 @@ class SmallVector // Can we simply steal the complete allocated storage? if (other.is_storage_allocated()) { - data_ptr_ = other.data_ptr_; - other.data_ptr_ = other.get_internal_array_pointer(); - - capacity_ = other.capacity_; - other.capacity_ = other.inner_capacity(); - - size_ = other.size_; - other.size_ = 0u; + data_ptr_ = std::exchange(other.data_ptr_, other.get_internal_array_pointer()); + capacity_ = std::exchange(other.capacity_, other.inner_capacity()); + size_ = std::exchange(other.size_, 0u); } else // otherwise fall back to moving (or at least copying) all elements { @@ -1574,8 +1570,7 @@ class SmallVector uninitialized_move_or_copy(b.begin(), b.end(), a.get_internal_array_pointer()); destroy_range(b.begin(), b.end()); - b.data_ptr_ = a.data_ptr_; - a.data_ptr_ = a.get_internal_array_pointer(); + b.data_ptr_ = std::exchange(a.data_ptr_, a.get_internal_array_pointer()); std::swap(a.capacity_, b.capacity_); std::swap(a.size_, b.size_);