From 5635a7ab26f42a3786713e3bbb72e00d1f6fef45 Mon Sep 17 00:00:00 2001 From: taltenbach <92919739+taltenbach@users.noreply.github.com> Date: Wed, 14 Oct 2026 15:07:47 +0200 Subject: [PATCH] Constrain variant value constructor to supported types (#1614) * Add docs and tutorial for etl::intrusive_avl_tree (#1608) * Add docs and tutorial for etl::intrusive_avl_tree Documents the intrusive AVL tree added in PR #1425: a full API reference page under Containers/Trees, and a tutorial demonstrating ordered access to intrusively-linked objects with unpredictable lifetimes, including move support and destructor-driven automatic removal from the tree. Co-Authored-By: Claude Sonnet 5 * Add complexity shortcode and document its usage Introduces a {{< complexity >}} Hugo shortcode (with light/dark styling for constant/logarithmic/linear/linearithmic/polynomial/exponential classes) for rendering Big-O complexity inline, applies it throughout the intrusive_avl_tree API reference in place of raw "O(...)" text, and documents the Description/Complexity structure and supported classes in DOCUMENTATION.md. Co-Authored-By: Claude Sonnet 5 * Fix issues found by @CodeRabbit * Put complexity notes on their own lines in intrusive_avl_tree docs Move the complexity shortcode from the inline "**Complexity:**" label to its own line under a "**Complexity**" label. Notes that give several alternatives (e.g. amortized vs. worst-case) now show each one on a separate line. Update the guideline in DOCUMENTATION.md to match. * Update complexity examples in DOCUMENTATION.md Add the hard line break after the "**Complexity**" label in the example, and rewrite the list of supported complexity classes in the new two-line format, including an example with several alternatives. * Add logarithmic complexity example to DOCUMENTATION.md --------- Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove * Updated sponsors pages * Refactor the documentation css to group styles for light and dark themes * Update Codacy badge in README.md Updated Codacy badge link to reflect new project status. * Fix Codacy badge display * Constrain variant value constructor to supported types Previously, the implicit value constructor of etl::variant could participate in overload resolution for unsupported types, potentially causing ambiguous calls before the assertion was triggered. The static_assert are replaced by enable_if constraints. Note the same approach is applied to other constructors and methods for consistency. --------- Co-authored-by: Sergei Co-authored-by: Claude Sonnet 5 Co-authored-by: John Wellbelove Co-authored-by: John Wellbelove --- include/etl/private/variant_variadic.h | 64 ++++++++++---------------- test/test_variant_variadic.cpp | 15 ++++++ 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 4161aa31..beae6b4b 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -1023,22 +1023,23 @@ namespace etl /// Construct from a value. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template , variant>::value && !Trivial_, int> = 0> + template < + typename T, bool Trivial_ = Is_Trivially_Destructible_Suite, + etl::enable_if_t, variant>::value && !Trivial_ && etl::is_one_of, TTypes...>::value, + int> = 0> ETL_CONSTEXPR14 variant(T&& value) : base_type(index_of_type::value) { - static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); - construct_in_place >(data, etl::forward(value)); } - template , variant>::value && Trivial_, int> = 0> + template < + typename T, bool Trivial_ = Is_Trivially_Destructible_Suite, + etl::enable_if_t, variant>::value && Trivial_ && etl::is_one_of, TTypes...>::value, + int> = 0> constexpr variant(T&& value) : base_type(etl::in_place_index_t::value>{}, etl::forward(value), index_of_type::value) { - static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); } #include "diagnostic_pop.h" @@ -1056,20 +1057,19 @@ namespace etl /// Construct from arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template = 0> + template , TTypes...>::value, int> = 0> ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, TArgs&&... args) : base_type(index_of_type::value) { - static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); - construct_in_place_args >(data, etl::forward(args)...); } - template = 0> + template , TTypes...>::value, int> = 0> constexpr explicit variant(etl::in_place_type_t, TArgs&&... args) : base_type(etl::in_place_index_t::value>{}, etl::remove_cvref_t(etl::forward(args)...), index_of_type::value) { - static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); } #include "diagnostic_pop.h" @@ -1077,22 +1077,20 @@ namespace etl /// Construct from arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template = 0> + template = 0> ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t, TArgs&&... args) : base_type(Index) { using type = type_from_index; - static_assert(etl::is_one_of::value, "Unsupported type"); - construct_in_place_args(data, etl::forward(args)...); } - template = 0> + template = 0> constexpr explicit variant(etl::in_place_index_t, TArgs&&... args) : base_type(etl::in_place_index_t{}, type_from_index(etl::forward(args)...), Index) { - using type = type_from_index; - static_assert(etl::is_one_of::value, "Unsupported type"); } #include "diagnostic_pop.h" @@ -1101,12 +1099,10 @@ namespace etl /// Construct from type, initializer_list and arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template + template , TTypes...>::value, int> = 0> ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, std::initializer_list init, TArgs&&... args) : base_type(index_of_type::value) { - static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); - construct_in_place_args >(data, init, etl::forward(args)...); } #include "diagnostic_pop.h" @@ -1115,13 +1111,11 @@ namespace etl /// Construct from index, initializer_list and arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template + template = 0> ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t, std::initializer_list init, TArgs&&... args) : base_type(Index) { using type = type_from_index; - static_assert(etl::is_one_of::value, "Unsupported type"); - construct_in_place_args(data, init, etl::forward(args)...); } #include "diagnostic_pop.h" @@ -1149,11 +1143,9 @@ namespace etl //*************************************************************************** /// Emplace by type with variadic constructor parameters. //*************************************************************************** - template + template ::value, int> = 0> T& emplace(TArgs&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) { - static_assert(etl::is_one_of::value, "Unsupported type"); - using type = etl::remove_cvref_t; do_destroy(); @@ -1168,12 +1160,10 @@ namespace etl //*************************************************************************** /// Emplace by type with variadic constructor parameters. //*************************************************************************** - template + template ::value, int> = 0> T& emplace(std::initializer_list il, TArgs&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, std::initializer_list, TArgs...>::value)) { - static_assert(etl::is_one_of::value, "Unsupported type"); - using type = etl::remove_cvref_t; do_destroy(); @@ -1188,12 +1178,10 @@ namespace etl //*************************************************************************** /// Emplace by index with variadic constructor parameters. //*************************************************************************** - template + template = 0> typename etl::variant_alternative_t >& emplace(TArgs&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) { - static_assert(Index < sizeof...(TTypes), "Index out of range"); - using type = type_from_index; do_destroy(); @@ -1208,12 +1196,10 @@ namespace etl //*************************************************************************** /// Emplace by index with variadic constructor parameters. //*************************************************************************** - template + template = 0> typename etl::variant_alternative_t >& emplace(std::initializer_list il, TArgs&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, std::initializer_list, TArgs...>::value)) { - static_assert(Index < sizeof...(TTypes), "Index out of range"); - using type = type_from_index; do_destroy(); @@ -1229,13 +1215,13 @@ namespace etl /// Move assignment operator for type. ///\param value The value to assign. //*************************************************************************** - template , variant>::value, int> = 0> + template < + typename T, + etl::enable_if_t, variant>::value && etl::is_one_of, TTypes...>::value, int> = 0> variant& operator=(T&& value) { using type = etl::remove_cvref_t; - static_assert(etl::is_one_of::value, "Unsupported type"); - do_destroy(); do_construct(etl::forward(value)); diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index 8e81a606..5cc33301 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -2932,6 +2932,21 @@ namespace CHECK(etl::get_if(&v1) != nullptr); } + //************************************************************************* + TEST(test_variant_implicit_conversion_constraints) + { + struct overloaded + { + overloaded(etl::variant&&) {} + overloaded(etl::variant&&) {} + }; + + /* Construct from value, trivially destructible suite */ + overloaded(1); + /* Construct from value, non-trivially destructible suite */ + overloaded(1.0f); + } + //************************************************************************* // Tests for noexcept properties of etl::variant // The noexcept specs only take effect when ETL_USING_EXCEPTIONS is enabled,