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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>
Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>

* 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 <sergej.shirokov@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
Co-authored-by: John Wellbelove <john.wellbelove@etlcpp.com>
This commit is contained in:
taltenbach 2026-10-14 15:07:47 +02:00 committed by GitHub
parent ebd9a7026f
commit 60ba92928e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 39 deletions

View File

@ -1016,22 +1016,23 @@ namespace etl
/// Construct from a value. /// Construct from a value.
//*************************************************************************** //***************************************************************************
#include "diagnostic_uninitialized_push.h" #include "diagnostic_uninitialized_push.h"
template <typename T, bool Trivial_ = Is_Trivially_Destructible_Suite, template <
etl::enable_if_t<!etl::is_same<etl::remove_cvref_t<T>, variant>::value && !Trivial_, int> = 0> typename T, bool Trivial_ = Is_Trivially_Destructible_Suite,
etl::enable_if_t<!etl::is_same<etl::remove_cvref_t<T>, variant>::value && !Trivial_ && etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value,
int> = 0>
ETL_CONSTEXPR14 variant(T&& value) ETL_CONSTEXPR14 variant(T&& value)
: base_type(index_of_type<T>::value) : base_type(index_of_type<T>::value)
{ {
static_assert(etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, "Unsupported type");
construct_in_place<etl::remove_cvref_t<T> >(data, etl::forward<T>(value)); construct_in_place<etl::remove_cvref_t<T> >(data, etl::forward<T>(value));
} }
template <typename T, bool Trivial_ = Is_Trivially_Destructible_Suite, template <
etl::enable_if_t<!etl::is_same<etl::remove_cvref_t<T>, variant>::value && Trivial_, int> = 0> typename T, bool Trivial_ = Is_Trivially_Destructible_Suite,
etl::enable_if_t<!etl::is_same<etl::remove_cvref_t<T>, variant>::value && Trivial_ && etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value,
int> = 0>
constexpr variant(T&& value) constexpr variant(T&& value)
: base_type(etl::in_place_index_t<index_of_type<T>::value>{}, etl::forward<T>(value), index_of_type<T>::value) : base_type(etl::in_place_index_t<index_of_type<T>::value>{}, etl::forward<T>(value), index_of_type<T>::value)
{ {
static_assert(etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, "Unsupported type");
} }
#include "diagnostic_pop.h" #include "diagnostic_pop.h"
@ -1049,20 +1050,19 @@ namespace etl
/// Construct from arguments. /// Construct from arguments.
//*************************************************************************** //***************************************************************************
#include "diagnostic_uninitialized_push.h" #include "diagnostic_uninitialized_push.h"
template <typename T, typename... TArgs, bool Trivial_ = Is_Trivially_Destructible_Suite, etl::enable_if_t<!Trivial_, int> = 0> template <typename T, typename... TArgs, bool Trivial_ = Is_Trivially_Destructible_Suite,
etl::enable_if_t<!Trivial_ && etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, int> = 0>
ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t<T>, TArgs&&... args) ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t<T>, TArgs&&... args)
: base_type(index_of_type<T>::value) : base_type(index_of_type<T>::value)
{ {
static_assert(etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, "Unsupported type");
construct_in_place_args<etl::remove_cvref_t<T> >(data, etl::forward<TArgs>(args)...); construct_in_place_args<etl::remove_cvref_t<T> >(data, etl::forward<TArgs>(args)...);
} }
template <typename T, typename... TArgs, bool Trivial_ = Is_Trivially_Destructible_Suite, etl::enable_if_t<Trivial_, int> = 0> template <typename T, typename... TArgs, bool Trivial_ = Is_Trivially_Destructible_Suite,
etl::enable_if_t<Trivial_ && etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, int> = 0>
constexpr explicit variant(etl::in_place_type_t<T>, TArgs&&... args) constexpr explicit variant(etl::in_place_type_t<T>, TArgs&&... args)
: base_type(etl::in_place_index_t<index_of_type<T>::value>{}, etl::remove_cvref_t<T>(etl::forward<TArgs>(args)...), index_of_type<T>::value) : base_type(etl::in_place_index_t<index_of_type<T>::value>{}, etl::remove_cvref_t<T>(etl::forward<TArgs>(args)...), index_of_type<T>::value)
{ {
static_assert(etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, "Unsupported type");
} }
#include "diagnostic_pop.h" #include "diagnostic_pop.h"
@ -1070,22 +1070,20 @@ namespace etl
/// Construct from arguments. /// Construct from arguments.
//*************************************************************************** //***************************************************************************
#include "diagnostic_uninitialized_push.h" #include "diagnostic_uninitialized_push.h"
template <size_t Index, typename... TArgs, bool Trivial_ = Is_Trivially_Destructible_Suite, etl::enable_if_t<!Trivial_, int> = 0> template <size_t Index, typename... TArgs, bool Trivial_ = Is_Trivially_Destructible_Suite,
etl::enable_if_t<!Trivial_ && (Index < sizeof...(TTypes)), int> = 0>
ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t<Index>, TArgs&&... args) ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t<Index>, TArgs&&... args)
: base_type(Index) : base_type(Index)
{ {
using type = type_from_index<Index>; using type = type_from_index<Index>;
static_assert(etl::is_one_of<type, TTypes...>::value, "Unsupported type");
construct_in_place_args<type>(data, etl::forward<TArgs>(args)...); construct_in_place_args<type>(data, etl::forward<TArgs>(args)...);
} }
template <size_t Index, typename... TArgs, bool Trivial_ = Is_Trivially_Destructible_Suite, etl::enable_if_t<Trivial_, int> = 0> template <size_t Index, typename... TArgs, bool Trivial_ = Is_Trivially_Destructible_Suite,
etl::enable_if_t<Trivial_ && (Index < sizeof...(TTypes)), int> = 0>
constexpr explicit variant(etl::in_place_index_t<Index>, TArgs&&... args) constexpr explicit variant(etl::in_place_index_t<Index>, TArgs&&... args)
: base_type(etl::in_place_index_t<Index>{}, type_from_index<Index>(etl::forward<TArgs>(args)...), Index) : base_type(etl::in_place_index_t<Index>{}, type_from_index<Index>(etl::forward<TArgs>(args)...), Index)
{ {
using type = type_from_index<Index>;
static_assert(etl::is_one_of<type, TTypes...>::value, "Unsupported type");
} }
#include "diagnostic_pop.h" #include "diagnostic_pop.h"
@ -1094,12 +1092,10 @@ namespace etl
/// Construct from type, initializer_list and arguments. /// Construct from type, initializer_list and arguments.
//*************************************************************************** //***************************************************************************
#include "diagnostic_uninitialized_push.h" #include "diagnostic_uninitialized_push.h"
template <typename T, typename U, typename... TArgs > template <typename T, typename U, typename... TArgs, etl::enable_if_t<etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, int> = 0>
ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t<T>, std::initializer_list<U> init, TArgs&&... args) ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t<T>, std::initializer_list<U> init, TArgs&&... args)
: base_type(index_of_type<T>::value) : base_type(index_of_type<T>::value)
{ {
static_assert(etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, "Unsupported type");
construct_in_place_args<etl::remove_cvref_t<T> >(data, init, etl::forward<TArgs>(args)...); construct_in_place_args<etl::remove_cvref_t<T> >(data, init, etl::forward<TArgs>(args)...);
} }
#include "diagnostic_pop.h" #include "diagnostic_pop.h"
@ -1108,13 +1104,11 @@ namespace etl
/// Construct from index, initializer_list and arguments. /// Construct from index, initializer_list and arguments.
//*************************************************************************** //***************************************************************************
#include "diagnostic_uninitialized_push.h" #include "diagnostic_uninitialized_push.h"
template <size_t Index, typename U, typename... TArgs > template <size_t Index, typename U, typename... TArgs, etl::enable_if_t<(Index < sizeof...(TTypes)), int> = 0>
ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t<Index>, std::initializer_list<U> init, TArgs&&... args) ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t<Index>, std::initializer_list<U> init, TArgs&&... args)
: base_type(Index) : base_type(Index)
{ {
using type = type_from_index<Index>; using type = type_from_index<Index>;
static_assert(etl::is_one_of<type, TTypes...>::value, "Unsupported type");
construct_in_place_args<type>(data, init, etl::forward<TArgs>(args)...); construct_in_place_args<type>(data, init, etl::forward<TArgs>(args)...);
} }
#include "diagnostic_pop.h" #include "diagnostic_pop.h"
@ -1142,11 +1136,9 @@ namespace etl
//*************************************************************************** //***************************************************************************
/// Emplace by type with variadic constructor parameters. /// Emplace by type with variadic constructor parameters.
//*************************************************************************** //***************************************************************************
template <typename T, typename... TArgs> template <typename T, typename... TArgs, etl::enable_if_t<etl::is_one_of<T, TTypes...>::value, int> = 0>
T& emplace(TArgs&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible<etl::remove_cvref_t<T>, TArgs...>::value)) T& emplace(TArgs&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible<etl::remove_cvref_t<T>, TArgs...>::value))
{ {
static_assert(etl::is_one_of<T, TTypes...>::value, "Unsupported type");
using type = etl::remove_cvref_t<T>; using type = etl::remove_cvref_t<T>;
do_destroy(); do_destroy();
@ -1161,12 +1153,10 @@ namespace etl
//*************************************************************************** //***************************************************************************
/// Emplace by type with variadic constructor parameters. /// Emplace by type with variadic constructor parameters.
//*************************************************************************** //***************************************************************************
template <typename T, typename U, typename... TArgs> template <typename T, typename U, typename... TArgs, etl::enable_if_t<etl::is_one_of<T, TTypes...>::value, int> = 0>
T& emplace(std::initializer_list<U> il, TArgs&&... args) T& emplace(std::initializer_list<U> il, TArgs&&... args)
ETL_NOEXCEPT_IF((etl::is_nothrow_constructible<etl::remove_cvref_t<T>, std::initializer_list<U>, TArgs...>::value)) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible<etl::remove_cvref_t<T>, std::initializer_list<U>, TArgs...>::value))
{ {
static_assert(etl::is_one_of<T, TTypes...>::value, "Unsupported type");
using type = etl::remove_cvref_t<T>; using type = etl::remove_cvref_t<T>;
do_destroy(); do_destroy();
@ -1181,12 +1171,10 @@ namespace etl
//*************************************************************************** //***************************************************************************
/// Emplace by index with variadic constructor parameters. /// Emplace by index with variadic constructor parameters.
//*************************************************************************** //***************************************************************************
template <size_t Index, typename... TArgs> template <size_t Index, typename... TArgs, etl::enable_if_t<(Index < sizeof...(TTypes)), int> = 0>
typename etl::variant_alternative_t<Index, variant<TTypes...> >& emplace(TArgs&&... args) typename etl::variant_alternative_t<Index, variant<TTypes...> >& emplace(TArgs&&... args)
ETL_NOEXCEPT_IF((etl::is_nothrow_constructible<type_from_index<Index>, TArgs...>::value)) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible<type_from_index<Index>, TArgs...>::value))
{ {
static_assert(Index < sizeof...(TTypes), "Index out of range");
using type = type_from_index<Index>; using type = type_from_index<Index>;
do_destroy(); do_destroy();
@ -1201,12 +1189,10 @@ namespace etl
//*************************************************************************** //***************************************************************************
/// Emplace by index with variadic constructor parameters. /// Emplace by index with variadic constructor parameters.
//*************************************************************************** //***************************************************************************
template <size_t Index, typename U, typename... TArgs> template <size_t Index, typename U, typename... TArgs, etl::enable_if_t<(Index < sizeof...(TTypes)), int> = 0>
typename etl::variant_alternative_t<Index, variant<TTypes...> >& emplace(std::initializer_list<U> il, TArgs&&... args) typename etl::variant_alternative_t<Index, variant<TTypes...> >& emplace(std::initializer_list<U> il, TArgs&&... args)
ETL_NOEXCEPT_IF((etl::is_nothrow_constructible<type_from_index<Index>, std::initializer_list<U>, TArgs...>::value)) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible<type_from_index<Index>, std::initializer_list<U>, TArgs...>::value))
{ {
static_assert(Index < sizeof...(TTypes), "Index out of range");
using type = type_from_index<Index>; using type = type_from_index<Index>;
do_destroy(); do_destroy();
@ -1222,13 +1208,13 @@ namespace etl
/// Move assignment operator for type. /// Move assignment operator for type.
///\param value The value to assign. ///\param value The value to assign.
//*************************************************************************** //***************************************************************************
template <typename T, etl::enable_if_t<!etl::is_same<etl::remove_cvref_t<T>, variant>::value, int> = 0> template <
typename T,
etl::enable_if_t<!etl::is_same<etl::remove_cvref_t<T>, variant>::value && etl::is_one_of<etl::remove_cvref_t<T>, TTypes...>::value, int> = 0>
variant& operator=(T&& value) variant& operator=(T&& value)
{ {
using type = etl::remove_cvref_t<T>; using type = etl::remove_cvref_t<T>;
static_assert(etl::is_one_of<type, TTypes...>::value, "Unsupported type");
do_destroy(); do_destroy();
do_construct<type>(etl::forward<T>(value)); do_construct<type>(etl::forward<T>(value));

View File

@ -3122,6 +3122,21 @@ namespace
CHECK(etl::get_if<int>(&v1) != nullptr); CHECK(etl::get_if<int>(&v1) != nullptr);
} }
//*************************************************************************
TEST(test_variant_implicit_conversion_constraints)
{
struct overloaded
{
overloaded(etl::variant<char, int>&&) {}
overloaded(etl::variant<float, NonTrivialNonMovable>&&) {}
};
/* 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 // Tests for noexcept properties of etl::variant
// The noexcept specs only take effect when ETL_USING_EXCEPTIONS is enabled, // The noexcept specs only take effect when ETL_USING_EXCEPTIONS is enabled,