From 231a903a4b2d6dbb88ef241997f7bef4ede199ea Mon Sep 17 00:00:00 2001 From: mrabine Date: Fri, 16 Oct 2026 21:15:33 +0200 Subject: [PATCH] Make variant trivially copyable when all alternatives are --- core/include/join/traits.hpp | 8 +++ core/include/join/variant.hpp | 96 +++++++++++++++++++++++++++++++++-- core/tests/variant_test.cpp | 16 ++++++ 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/core/include/join/traits.hpp b/core/include/join/traits.hpp index 371a0ca3..e1494221 100644 --- a/core/include/join/traits.hpp +++ b/core/include/join/traits.hpp @@ -276,6 +276,14 @@ namespace join { }; + /** + * @brief check if types in a parameter pack are trivially copyable. + */ + template + struct are_trivially_copyable : std::integral_constant::value...>::value> + { + }; + /** * @brief allow construction when default constructor is disabled. */ diff --git a/core/include/join/variant.hpp b/core/include/join/variant.hpp index becead7f..50acbb47 100644 --- a/core/include/join/variant.hpp +++ b/core/include/join/variant.hpp @@ -224,7 +224,7 @@ namespace join * @brief helper class representing a variant storage in order to be able to disable default/copy/move * constructors/operators. */ - template + template struct VariantStorage { /** @@ -337,6 +337,96 @@ namespace join /// index of the alternative that is currently held by the variant. std::size_t _which = 0; }; + + /** + * @brief variant storage of trivially copyable alternatives, trivially copyable itself. + */ + template + struct VariantStorage + { + /** + * @brief default constructor. + */ + constexpr VariantStorage () + : VariantStorage (in_place_index_t<0>{}) + { + } + + /** + * @brief constructs a variant storage with the alternative T specified by the index I. + * @param args arguments to initialize the contained value with. + */ + template + constexpr explicit VariantStorage (in_place_index_t, Args&&... args) + : _which (I) + { + new (storage ()) find_element_t (std::forward (args)...); + } + + /** + * @brief copy constructor. + * @param other object to copy. + */ + constexpr VariantStorage (const VariantStorage& other) = default; + + /** + * @brief move constructor. + * @param other object to move. + */ + constexpr VariantStorage (VariantStorage&& other) = default; + + /** + * @brief destroy the VariantStorage instance. + */ + ~VariantStorage () = default; + + /** + * @brief copy assignment. + * @param other object to copy. + * @return a reference of the current object. + */ + constexpr VariantStorage& operator= (const VariantStorage& other) = default; + + /** + * @brief move assignment. + * @param other object to move. + * @return a reference of the current object. + */ + constexpr VariantStorage& operator= (VariantStorage&& other) = default; + + /** + * @brief get storage address. + * @return storage address. + */ + constexpr void* storage () + { + return static_cast (std::addressof (_data)); + } + + /** + * @brief get storage address. + * @return storage address. + */ + constexpr const void* storage () const + { + return static_cast (std::addressof (_data)); + } + + /** + * @brief swap this storage with other. + * @param other storage to swap with. + */ + void swap (VariantStorage& other) noexcept + { + std::swap (*this, other); + } + + /// aligned storage. + alignas (Ts...) unsigned char _data[std::max ({sizeof (Ts)...})]; + + /// index of the alternative that is currently held by the variant. + std::size_t _which = 0; + }; } /** @@ -344,7 +434,7 @@ namespace join */ template class Variant - : private details::VariantStorage, + : private details::VariantStorage::value, Ts...>, private EnableDefault::value, Variant>, private std::_Enable_copy_move::value, are_copy_assignable::value, are_move_constructible::value, are_move_assignable::value, @@ -356,7 +446,7 @@ namespace join static_assert (all::value...>::value, "Variant must have no array alternative."); static_assert (all::value...>::value, "Variant must have no reference alternative"); - using Base = details::VariantStorage; + using Base = details::VariantStorage::value, Ts...>; using DefaultEnabler = EnableDefault::value, Variant>; /** diff --git a/core/tests/variant_test.cpp b/core/tests/variant_test.cpp index 5a0f6303..695b2cc6 100644 --- a/core/tests/variant_test.cpp +++ b/core/tests/variant_test.cpp @@ -46,6 +46,22 @@ TEST (Variant, is_first_default_constructible) EXPECT_FALSE (can); } +/** + * @brief test triviality. + */ +TEST (Variant, trivial) +{ + EXPECT_TRUE ((std::is_trivially_copyable>::value)); + EXPECT_TRUE ((std::is_trivially_destructible>::value)); + EXPECT_FALSE ((std::is_trivially_copyable>::value)); + EXPECT_FALSE ((std::is_trivially_destructible>::value)); + + Variant a (2.5), b (true); + a.swap (b); + EXPECT_TRUE (a.is ()); + EXPECT_EQ (b.get (), 2.5); +} + /** * @brief Test default construction. */