diff --git a/doc/rvariant.adoc b/doc/rvariant.adoc index 63b2c1c..f5838cb 100644 --- a/doc/rvariant.adoc +++ b/doc/rvariant.adoc @@ -25,6 +25,7 @@ Yaito Kakeyama; Nana Sakisaka :unwrap_recursive_type: <> :unwrap_recursive: pass:quotes[xref:#rvariant.recursive.helper[unwrap_recursive]] :recursive_wrapper: pass:macros[xref:#rvariant.recursive[recursive_wrapper]] +:recursive_wrapper_alloca: pass:macros[xref:#rvariant.recursive[recursive_wrapper_alloca]] :subset_of: <> :equivalent_to: <> @@ -35,12 +36,12 @@ Yaito Kakeyama; Nana Sakisaka ---- // A common pattern for representing recursive ASTs using recursive variants. struct BinaryExpr; -using Expr = temp_ns::<>>>; +using Expr = iris::<>>>; enum class Op; struct BinaryExpr { Expr lhs, rhs; Op op{}; }; Expr expr{BinaryExpr{Expr{42}, Expr{3.14}}}; -expr.visit(temp_ns::overloaded{ +expr.visit(iris::overloaded{ [](int const&) { /* ... */ }, [](double const&) { /* ... */ }, [](BinaryExpr const&) { /* ... */ }, @@ -179,9 +180,9 @@ using A = int; using B = double; struct C {}; -using AB = temp_ns::<>; -using BA = temp_ns::rvariant; -using ABC = temp_ns::rvariant; +using AB = iris::<>; +using BA = iris::rvariant; +using ABC = iris::rvariant; // <> AB ab{42}; @@ -196,30 +197,30 @@ ab<><0>(123); // <> ab = AB{123}; -A& a = temp_ns::<><0>(ab); -A& a = temp_ns::get(ab); -C& c = temp_ns::get(ab); // throws {bad-variant-access} +A& a = iris::<><0>(ab); +A& a = iris::get(ab); +C& c = iris::get(ab); // throws {bad-variant-access} -A* a = temp_ns::<><0>(&ab); -A* a = temp_ns::get_if(&ab); -C* c = temp_ns::get_if(&ab); // `nullptr` +A* a = iris::<><0>(&ab); +A* a = iris::get_if(&ab); +C* c = iris::get_if(&ab); // `nullptr` // compatibility with boost; same effect as `get_if` -A* a = temp_ns::<><0>(&ab); -A* a = temp_ns::get(&ab); -C* c = temp_ns::get(&ab); // `nullptr` +A* a = iris::<><0>(&ab); +A* a = iris::get(&ab); +C* c = iris::get(&ab); // `nullptr` https://eel.is/{cxx}draft/variant.relops#lib:operator==,variant[ab pass:quotes[==] ab]; https://eel.is/{cxx}draft/variant.relops#lib:operator<,variant[ab pass:quotes[<] ab]; https://eel.is/{cxx}draft/variant.relops#lib:operator<=>,variant[ab pass:quotes[<=>] ab]; // `requires (https://en.cppreference.com/w/cpp/utility/compare/three_way_comparable[std::three_way_comparable] && ...)` -auto visitor = temp_ns::overloaded { +auto visitor = iris::overloaded { [](A const& a) {}, [](B const& b) {}, }; ab<>(visitor); // member visit -temp_ns::<>(visitor, ab); // function visit +iris::<>(visitor, ab); // function visit ---- @@ -237,9 +238,9 @@ temp_ns::<>(visitor, ab); // function visit abc = AB{}; // subset assignment } -static_assert(temp_ns::<> == 2); -static_assert(std::same_as><0, AB>, A>); -static_assert(temp_ns::<>(ab)); +static_assert(iris::<> == 2); +static_assert(std::same_as><0, AB>, A>); +static_assert(iris::<>(ab)); static_assert(!ab<>()); static_assert(ab<>() != https://eel.is/c++draft/variant.syn[std::variant_npos]); @@ -259,7 +260,7 @@ std::size_t _ = <>(ab); // compatibility with boost // <> { - using V = ::temp_ns::rvariant; + using V = ::iris::rvariant; // <> std::cout << V{42} << '\n'; // prints pass:quotes[`42`] @@ -267,9 +268,9 @@ std::size_t _ = <>(ab); // compatibility with boost // <> std::println("{}", V{42}); // prints pass:quotes[`42`] - constexpr auto v_fmt = temp_ns::variant_format_for("{:04d}", "{:.1f}"); - std::println("foo{}bar", temp_ns::format_by(v_fmt, V(42)); // prints pass:quotes[`foo0042bar`] - std::println("foo{}bar", temp_ns::format_by(v_fmt, V(3.14)); // prints pass:quotes[`foo3.1bar`] + constexpr auto v_fmt = iris::variant_format_for("{:04d}", "{:.1f}"); + std::println("foo{}bar", iris::format_by(v_fmt, V(42)); // prints pass:quotes[`foo0042bar`] + std::println("foo{}bar", iris::format_by(v_fmt, V(3.14)); // prints pass:quotes[`foo3.1bar`] } ---- @@ -284,25 +285,21 @@ std::size_t _ = <>(ab); // compatibility with boost [[rvariant.syn]] -== Header synopsis [.slug]##<>## +== Header synopsis [.slug]##<>## [,cpp,subs="+macros,+attributes"] ---- -#include -#include // for pass:quotes[`std::allocator`], etc. -#include // for pass:quotes[`std::add_pointer`], etc. -#include // for pass:quotes[`std::in_place_type`], etc. -#include // for compatibility with {bad-variant-access}, etc. - -namespace temp_ns { +namespace iris { // <>, class template pass:quotes[`rvariant`] template class rvariant; // <>, class template pass:quotes[`recursive_wrapper`] -template> +template class recursive_wrapper; +template> +class recursive_wrapper_alloca; /* all features commented below defined as per https://eel.is/c+\+draft/variant[[variant\]] */ // variant_size, variant_size_v @@ -376,20 +373,16 @@ template /* constexpr */ std::size_t hash_value(rvariant const&); // <>, hash support +template + /* constexpr */ std::size_t hash_value(recursive_wrapper const&); template - /* constexpr */ std::size_t hash_value(recursive_wrapper const&); + /* constexpr */ std::size_t hash_value(recursive_wrapper_alloca const&); // <>, pass:quotes[`recursive_wrapper`] helper classes template using unwrap_recursive_type = {see-below}; template constexpr auto&& unwrap_recursive(T&& o) noexcept; -// <>, pack manipulation and deduping -template class TT, class A, class B> - struct compact_alternative; -template class TT, class A, class B> - using compact_alternative_t = typename compact_alternative::type; - -} // temp_ns +} // iris ---- [,cpp,subs="+macros,+attributes"] @@ -397,10 +390,10 @@ template class TT, class A, class B> namespace std { // <>, hash support -template struct hash<::temp_ns::rvariant>; +template struct hash<::iris::rvariant>; // <>, hash support -template struct hash<::temp_ns::recursive_wrapper>; +template struct hash<::iris::recursive_wrapper>; } // std ---- @@ -411,7 +404,7 @@ template struct hash<::temp_ns::recursive_wrapper class rvariant @@ -486,7 +479,7 @@ public: constexpr R visit(this Self&&, Visitor&&); }; -} // temp_ns +} // iris ---- @@ -499,9 +492,9 @@ NOTE: See also: spec of https://eel.is/c++draft/variant[`std::variant`] and http * All types in `Ts` must satisfy all requirements on the corresponding parameter in `std::variant`, unless otherwise noted. -* [.underline]#Let `T` and `A` denote arbitrary types. For the template parameter of `rvariant`, if a user provides both `T` and `{recursive_wrapper}` , the program is ill-formed.# +* [.underline]#Let `T` denote an arbitrary type. For the template parameter of `rvariant`, if a user provides both `T` and any instantiation of `{recursive_wrapper}` or `{recursive_wrapper_alloca}` that has the `value_type` of `T`, the program is ill-formed.# -* [.underline]#Let `T` denote an arbitrary type. For the template parameter of `rvariant`, if a user provides multiple different specializations of `{recursive_wrapper}` such that the first template parameter is `T`, the program is ill-formed.# +* [.underline]#Let `T` denote an arbitrary type. For the template parameter of `rvariant`, if a user provides multiple different instantiations of `{recursive_wrapper_alloca}` such that the first template parameter is `T`, the program is ill-formed.# [WARNING] -- @@ -510,13 +503,14 @@ Although `rvariant` is normally capable of holding duplicate alternatives, the a [,cpp,subs="+macros,+attributes"] ---- rvariant< - int, recursive_wrapper, recursive_wrapper> + int, recursive_wrapper, recursive_wrapper_alloca> > v(42); // error-prone; not allowed ---- -- -* Let `VT~_i_~` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT~_i_~` denote `T~_i_~`. Let `U~_j_~` denote the _j_^th^ type of the template parameter pack having the name `Us` on each flexibility-related functions. [.underline]#The _corresponding alternative_ for `rvariant` is the first type for which `std::is_same_v<{unwrap_recursive_type}, {unwrap_recursive_type}>` is `true`#. +* If `{recursive_wrapper}` or `{recursive_wrapper_alloca}` (with any type `A`) occurs anywhere in `Ts\...`, let `VT~_i_~` denote that type; otherwise, let `VT~_i_~` denote `T~_i_~`. Let `U~_j_~` denote the _j_^th^ type of the template parameter pack having the name `Us` on each flexibility-related functions. [.underline]#The _corresponding alternative_ for `rvariant` is the first type for which `std::is_same_v<{unwrap_recursive_type}, {unwrap_recursive_type}>` is `true`#. +* For the function that has the formal template parameter named `T`: if `{recursive_wrapper}` or `{recursive_wrapper_alloca}` (with any type `A`) occurs anywhere in `Ts\...`, let `VT` denote that type; otherwise, let `VT` denote `T`. [[rvariant.ctor]] === Constructors [.slug]##<>## @@ -563,9 +557,7 @@ include::_std-variant-proxy.adoc[] + *_Postconditions:_* `holds_alternative[.underline]##<{unwrap_recursive_type}>##(*this)` is `true`. -* [.candidate]#5)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}`#. -+ -[.underline]#Let `VT` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT` denote `T`#. +* [.candidate]#5)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`#. + *_Constraints:_* + @@ -583,9 +575,7 @@ include::_std-variant-proxy.adoc[] + *_Remarks:_* If [.underline]#`VT`#'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor. -* [.candidate]#6)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}`#. -+ -[.underline]#Let `VT` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT` denote `T`.# +* [.candidate]#6)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`#. + *_Constraints:_* + @@ -785,9 +775,7 @@ constexpr variant_alternative_t>& [.candidates] :spec-url: https://eel.is/c++draft/variant.mod -* [.candidate]#1)# [.underline]#Let `VT` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT` denote `T`.# -+ -*_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}`.# +* [.candidate]#1)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# + *_Constraints:_* `std::is_constructible_v<[.underline]##VT##, Args\...>` is `true`, and `T` occurs exactly once in [.underline]#`{unwrap_recursive_type}`#. + @@ -795,9 +783,7 @@ constexpr variant_alternative_t>& pass:quotes[  ]`return emplace<__I__>(std::forward(args)\...);` + where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. -* [.candidate]#2)# [.underline]#Let `VT` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT` denote `T`.# -+ -*_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}`.# +* [.candidate]#2)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# + *_Constraints:_* `std::is_constructible_v<[.underline]##VT##, std::initializer_list&, Args\...>` is `true`, and `T` occurs exactly once in [.underline]#`{unwrap_recursive_type}`#. + @@ -809,13 +795,13 @@ where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_ty + *_Returns:_* [.underline]#Let `o` denote# a reference to the new contained value. [.underline]#Returns `{unwrap_recursive}(o)`.# + -*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `std::forward(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)# +*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `std::forward(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)# * [.candidate]#4)# Equivalent to the `std::variant` counterpart, ^https://eel.is/c++draft/variant.mod[[spec\]]^ except: + *_Returns:_* [.underline]#Let `o` denote# a reference to the new contained value. [.underline]#Returns `{unwrap_recursive}(o)`.# + -*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `il, std::forward(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)# +*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `il, std::forward(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)# [[rvariant.status]] === Value status [.slug]##<>## @@ -910,7 +896,7 @@ include::_std-variant-proxy.adoc[] [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template struct variant_alternative; // not defined @@ -921,7 +907,7 @@ struct variant_alternative;pass:quotes[[.candidate\]#// 1#] template struct variant_alternative>;pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -939,7 +925,7 @@ include::_std-variant-proxy.adoc[] [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns::rvariant_set { +namespace iris::rvariant_set { template struct is_subset_of : std::false_type {};pass:quotes[[.candidate\]#// 1#] @@ -956,7 +942,7 @@ concept subset_of = is_subset_of_v; template concept equivalent_to = subset_of && subset_of; -} // temp_ns::rvariant_set +} // iris::rvariant_set ---- [.candidates] @@ -974,12 +960,12 @@ concept equivalent_to = subset_of && subset_of; [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr bool holds_alternative(rvariant const& v) noexcept; -} // temp_ns +} // iris ---- [.candidates] @@ -987,7 +973,7 @@ constexpr bool holds_alternative(rvariant const& v) noexcept; + *_Returns:_* `true` if `v.index()` is equal to the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. + -*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}`.# +*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# [,cpp,subs="+macros,+attributes"] @@ -1010,12 +996,12 @@ constexpr {see-below} const&& pass:quotes[_GET_](rvariant const&& v); // + *_Preconditions:_* `v.index()` is `I`. + -*_Returns:_* [.underline]#`o`, where `o` denotes a reference to the object stored in `v`, if the type of the expression's receiver is a specialization of `{recursive_wrapper}`; otherwise, returns `{unwrap_recursive}(o)`#. +*_Returns:_* [.underline]#`o`, where `o` denotes a reference to the object stored in `v`, if the type of the expression's receiver is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`; otherwise, returns `{unwrap_recursive}(o)`#. [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr variant_alternative_t>& @@ -1033,7 +1019,7 @@ template constexpr variant_alternative_t> const&& get(rvariant const&& v); -} // temp_ns +} // iris ---- [.candidates] @@ -1044,14 +1030,14 @@ constexpr variant_alternative_t> const&& [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr T& get(rvariant& v); template constexpr T&& get(rvariant&& v); template constexpr T const& get(rvariant const& v); template constexpr T const&& get(rvariant const&& v); -} // temp_ns +} // iris ---- [.candidates] @@ -1059,12 +1045,12 @@ template constexpr T const&& get(rvariant const&& v + *_Effects:_* [.underline]#Let `VT` denote the type of the alternative held by `v`. If `{unwrap_recursive_type}` is the same type as `T`#, returns [.underline]#`{unwrap_recursive}(o)`, where `o` denotes a reference to the object stored in the `rvariant`#. Otherwise, throws an exception of type {bad-variant-access}. + -*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}`.# +*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr std::add_pointer_t>> @@ -1074,7 +1060,7 @@ template constexpr std::add_pointer_t> const> get_if(rvariant const* v) noexcept;pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -1085,7 +1071,7 @@ constexpr std::add_pointer_t> const> [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr std::add_pointer_t @@ -1095,7 +1081,7 @@ template constexpr std::add_pointer_t get_if(rvariant const* v) noexcept;pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -1103,7 +1089,7 @@ constexpr std::add_pointer_t + *_Effects:_* Equivalent to: `return get_if<__i__>(v);` with _i_ being the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. + -*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}`.# +*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# [[rvariant.visit]] @@ -1111,7 +1097,7 @@ constexpr std::add_pointer_t [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr {see-below} visit(Visitor&& vis, Variants&&... vars);pass:quotes[[.candidate\]#// 1#] @@ -1119,7 +1105,7 @@ constexpr {see-below} visit(Visitor&& vis, Variants&&... vars);pass:quotes[[.can template constexpr R visit(Visitor&& vis, Variants&&... vars);pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris // below are member functions of the class template <>: @@ -1136,7 +1122,7 @@ constexpr R visit(this Self&& self, Visitor&& vis);pass:quotes[[.candidate\]#// [none] ** -- `_GET_<__m__>(std::forward(vars))` is replaced with `{unwrap_recursive}(_GET_<__m__>(std::forward(vars)))`. -* [.candidate]#3-4)# Equivalent to the `std::variant` counterpart ^https://eel.is/c++draft/variant.visit[[spec\]]^, except that it forwards to `temp_ns::visit` instead of `std::visit`. +* [.candidate]#3-4)# Equivalent to the `std::variant` counterpart ^https://eel.is/c++draft/variant.visit[[spec\]]^, except that it forwards to `iris::visit` instead of `std::visit`. [[rvariant.hash]] @@ -1147,57 +1133,66 @@ constexpr R visit(this Self&& self, Visitor&& vis);pass:quotes[[.candidate\]#// namespace std { template -struct hash<::temp_ns::rvariant>;pass:quotes[[.candidate\]#// 1#] +struct hash<::iris::rvariant>;pass:quotes[[.candidate\]#// 1#] + +template +struct hash<::iris::recursive_wrapper>;pass:quotes[[.candidate\]#// 2#] template -struct hash<::temp_ns::recursive_wrapper>;pass:quotes[[.candidate\]#// 2#] +struct hash<::iris::recursive_wrapper_alloca>;pass:quotes[[.candidate\]#// 3#] } // std ---- +[.candidates] +-- +:spec-url: https://eel.is/c++draft/variant.hash +* [.candidate]#1)# +include::_std-variant-proxy.adoc[] + +:spec-url: https://eel.is/c++draft/indirect.hash +* [.candidate]#2-3)# +include::_std-indirect-proxy.adoc[] +-- + [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template -/* constexpr */ std::size_t hash_value(rvariant const& v);pass:quotes[[.candidate\]#// 3#] +/* constexpr */ std::size_t hash_value(rvariant const& v);pass:quotes[[.candidate\]#// 1#] + +template +/* constexpr */ std::size_t hash_value(recursive_wrapper const& rw);pass:quotes[[.candidate\]#// 2#] template -/* constexpr */ std::size_t hash_value(recursive_wrapper const& rw);pass:quotes[[.candidate\]#// 4#] +/* constexpr */ std::size_t hash_value(recursive_wrapper_alloca const& rw);pass:quotes[[.candidate\]#// 3#] -} // temp_ns +} // iris ---- [.candidates] -- -:spec-url: https://eel.is/c++draft/variant.hash -* [.candidate]#1)# -include::_std-variant-proxy.adoc[] +* [.candidate]#1)# *_Effects:_* Equivalent to `std::hash>{}(v)`. -:spec-url: https://eel.is/c++draft/indirect.hash -* [.candidate]#2)# -include::_std-indirect-proxy.adoc[] +* [.candidate]#2)# *_Effects:_* Equivalent to `std::hash>{}(rw)`. -* [.candidate]#3)# *_Effects:_* Equivalent to `std::hash>{}(v)`. - -* [.candidate]#4)# *_Effects:_* Equivalent to `std::hash>{}(rw)`. +* [.candidate]#3)# *_Effects:_* Equivalent to `std::hash>{}(rw)`. -- [[rvariant.io]] == I/O [.slug]##<>## -I/O components are _not_ included by the global convenience header (``). +I/O components are _not_ included by the global convenience header (``). [[rvariant.io.ostream]] === `operator<<` support [,cpp,subs="+macros,+attributes"] ---- -// - -#include +// -namespace temp_ns { +namespace iris { template constexpr bool pass:quotes[_ADL-ostreamable_] = {see-below}; // {exposition-only}pass:quotes[[.candidate\]#// 1#] @@ -1205,7 +1200,7 @@ constexpr bool pass:quotes[_ADL-ostreamable_] = {see-below}; // {exposition-only template std::ostream& operator<<(std::ostream& os, rvariant const& v);pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -1239,7 +1234,7 @@ std::ostream& operator<<(std::ostream& os, rvariant const& v);pass:quotes [none] * Let `v` denote an object of `rvariant`, and let `proxy` denote an object of `{variant-format-proxy}`. -* The specialization `std::formatter<::temp_ns::rvariant, charT>` (for arbitrary `charT`) is enabled if and only if `std::formattable<{unwrap_recursive_type}, charT>` is `true` for all _i_, with the following characteristics: +* The specialization `std::formatter<::iris::rvariant, charT>` (for arbitrary `charT`) is enabled if and only if `std::formattable<{unwrap_recursive_type}, charT>` is `true` for all _i_, with the following characteristics: + [none] ** -- The format specifier must be empty, otherwise `std::format_error` is thrown, and @@ -1249,7 +1244,7 @@ std::ostream& operator<<(std::ostream& os, rvariant const& v);pass:quotes + [,cpp,subs="+macros,+attributes"] ---- -std::println("{}", temp_ns::rvariant(42)); // prints pass:quotes[`42`] +std::println("{}", iris::rvariant(42)); // prints pass:quotes[`42`] ---- * The specialization `std::formatter<{variant-format-proxy}, charT>` is enabled if and only if: @@ -1269,10 +1264,10 @@ std::println("{}", temp_ns::rvariant(42)); // prints pass:quotes[`4 + [,cpp,subs="+macros,+attributes"] ---- -using V = temp_ns::rvariant; -constexpr auto v_fmt = temp_ns::variant_format_for("{:04d}", "{:.1f}"); -std::println("foo{}bar", temp_ns::format_by(v_fmt, V(42)); // prints pass:quotes[`foo0042bar`] -std::println("foo{}bar", temp_ns::format_by(v_fmt, V(3.14)); // prints pass:quotes[`foo3.1bar`] +using V = iris::rvariant; +constexpr auto v_fmt = iris::variant_format_for("{:04d}", "{:.1f}"); +std::println("foo{}bar", iris::format_by(v_fmt, V(42)); // prints pass:quotes[`foo0042bar`] +std::println("foo{}bar", iris::format_by(v_fmt, V(3.14)); // prints pass:quotes[`foo3.1bar`] ---- @@ -1288,9 +1283,7 @@ using pass:quotes[_select-char-t_] = {see-below}; // {exposition-only} [,cpp,subs="+macros,+attributes"] ---- -// - -#include +// template struct {variant-format-string} // {exposition-only} @@ -1299,7 +1292,7 @@ struct {variant-format-string} // {exposition-only} auto const& operator()(std::in_place_type) const noexcept { return fmts...[i]; } }; -namespace temp_ns { +namespace iris { template constexpr {variant-format-string}<{see-below}> variant_format(Fmts&&... fmts) noexcept;pass:quotes[[.candidate\]#// 1#] @@ -1307,7 +1300,7 @@ constexpr {variant-format-string}<{see-below}> variant_format(Fmts&&... fmts) no template constexpr {variant-format-string}<{see-below}> variant_format_for(Fmts&&... fmts) noexcept;pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -1326,9 +1319,7 @@ Let `charT` denote `_select-char-t_`. [,cpp,subs="+macros,+attributes"] ---- -// - -#include +// template struct {variant-format-proxy} // {exposition-only} @@ -1337,13 +1328,13 @@ struct {variant-format-proxy} // {exposition-only} Variant v; }; -namespace temp_ns { +namespace iris { template constexpr {variant-format-proxy} format_by(VFormat&& v_fmt, Variant&& v) noexcept;pass:quotes[[.candidate\]#// 1#] -} // temp_ns +} // iris ---- [.candidates] @@ -1357,64 +1348,73 @@ format_by(VFormat&& v_fmt, Variant&& v) noexcept;pass:quotes[[.candidate\]#// 1# [,cpp,subs="+macros,+attributes"] ---- -#include -#include +namespace iris { + +template +class recursive_wrapper +{ + {see-below} +}; + +} // iris +---- -namespace temp_ns { +Class template `recursive_wrapper` behaves like `recursive_wrapper_alloca` instantiated with `std::allocator`, except that the allocator-related member functions (that is, member functions that take allocator-specific arguments) are omitted. +(_Note:_ this is a QoL feature to discard the rarely used `std::allocator` type parameter from diagnostic messages; see https://github.com/iris-cpp/iris/issues/43[iris-cpp/iris#43].) + +[,cpp,subs="+macros,+attributes"] +---- +namespace iris { template> -class recursive_wrapper +class recursive_wrapper_alloca { // provides the same functionality as https://eel.is/c+\+draft/indirect[pass:quotes[`std::indirect`]], unless otherwise noted // <>, constructors - constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(); + constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper_alloca(); template - constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(U&& x); + constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper_alloca(U&& x); }; // equivalent to the https://eel.is/c+\+draft/indirect[pass:quotes[`std::indirect`]] counterpart template - recursive_wrapper(Value) -> recursive_wrapper; + recursive_wrapper_alloca(Value) -> recursive_wrapper_alloca; // equivalent to the https://eel.is/c++draft/indirect[pass:quotes[`std::indirect`]] counterpart template - recursive_wrapper(std::allocator_arg_t, Allocator, Value) - -> recursive_wrapper< + recursive_wrapper_alloca(std::allocator_arg_t, Allocator, Value) + -> recursive_wrapper_alloca< Value, typename std::allocator_traits::template rebind_alloc >; -} // temp_ns +} // iris ---- [,cpp,subs="+macros,+attributes"] ---- -// - -#include +// -namespace temp_ns::pmr { +namespace iris::pmr { template -using recursive_wrapper = ::temp_ns::recursive_wrapper>; +using recursive_wrapper = ::iris::recursive_wrapper_alloca< + T, std::pmr::polymorphic_allocator +>; -} // temp_ns::pmr +} // iris::pmr ---- [[rvariant.recursive.general]] === General [.slug]##<>## -Unless otherwise noted, the class template `temp_ns::recursive_wrapper` and relevant components in the namespace scope provide same functionality and have equivalent requirements as `std::indirect`, except that: +Unless otherwise noted, the class template `recursive_wrapper_alloca` and relevant components in the namespace scope provide same functionality and have equivalent requirements as `std::indirect`. -[none] -* -- The class name is `recursive_wrapper`. -* -- `std::indirect` and `temp_ns::recursive_wrapper` are distinguishable in type level. - -WARNING: `temp_ns::recursive_wrapper` is *not* a type alias of `std::indirect` and does *not* publicly derive from it. +WARNING: `recursive_wrapper` and `recursive_wrapper_alloca` are *not* type alias of `std::indirect` and do *not* publicly derive from it. -NOTE: Although `std::indirect` is a {cpp}26 feature, `temp_ns::recursive_wrapper` can be used in {cpp}23. +NOTE: Although `std::indirect` is a {cpp}26 feature, `recursive_wrapper` and `recursive_wrapper_alloca` can be used in {cpp}23. [[rvariant.recursive.ctor]] @@ -1423,10 +1423,10 @@ Effectively overrides only the ones listed below; rest are the same as `std::ind [,cpp,subs="+macros,+attributes"] ---- -constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper();pass:quotes[[.candidate\]#// 1#] +constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper_alloca();pass:quotes[[.candidate\]#// 1#] template -constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(U&& u);pass:quotes[[.candidate\]#// 2#] +constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper_alloca(U&& u);pass:quotes[[.candidate\]#// 2#] ---- [.candidates] @@ -1436,7 +1436,7 @@ constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(U&& u + -- [none] -** -- `std::is_same_v, recursive_wrapper>` is `false`, and +** -- `std::is_same_v, recursive_wrapper_alloca>` is `false`, and ** -- `std::is_same_v, std::in_place_t>` is `false`, and ** -- `std::is_default_constructible_v` is `true`, and ** -- [.underline]#`std::is_convertible_v` is `true`#. @@ -1445,7 +1445,7 @@ constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(U&& u _Note 1:_ [.underline]#This prevents recursive instantiation of `std::is_constructible`#, even for recursive types, while preserving SFINAE-friendliness. This specification is technically viable only because the class template `rvariant` never uses `std::is_convertible` in any of its constructor overloads. As a result, the atomic constraints of `rvariant` and `recursive_wrapper` remain mutually exclusive. However, if a user-defined class depends on _both_ `std::is_constructible` and `std::is_convertible` (for the same `rvariant` specialization), it may trigger recursive instantiation. + -_Note 2:_ It is currently unknown whether the recursive instantiation scenario described in _Note 1_ can be technically avoided. This note is provided for informational purposes only and does not specify the semantics of `recursive_wrapper`. +_Note 2:_ It is currently unknown whether the recursive instantiation scenario described in _Note 1_ can be technically avoided without depending on the fragile mutual-exclusiveness on `std::is_constructible` and `std::is_convertible`. If you are aware of any technical insights, please contact us at https://github.com/iris-cpp/iris/issues[iris-cpp/iris]. -- + *_Effects:_* Equivalent to the `std::indirect` counterpart. ^link:pass:[https://eel.is/c++draft/indirect.ctor#lib:indirect,constructor______][[spec\]]^ @@ -1456,102 +1456,33 @@ _Note 2:_ It is currently unknown whether the recursive instantiation scenario d [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template using unwrap_recursive_type = {see-below}; -} // temp_ns +} // iris ---- [.candidates] -* [.candidate]#{empty}# Denotes `T::value_type` if `T` is a specialization of `{recursive_wrapper}`. Otherwise, denotes `T`. +* [.candidate]#{empty}# Denotes `T::value_type` if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`. Otherwise, denotes `T`. [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr auto&& unwrap_recursive(T&& o) noexcept; -} // temp_ns +} // iris ---- [.candidates] -* [.candidate]#{empty}# *_Returns:_* `*o`, if cv-unqualified non-reference type for `T` is a specialization of `{recursive_wrapper}`. Otherwise, returns `o`. +* [.candidate]#{empty}# *_Returns:_* `*o`, if cv-unqualified non-reference type for `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`. Otherwise, returns `o`. + *_Remarks:_* `unwrap_recursive` is an _algorithm function object_ (link:https://eel.is/c++draft/alg.func.obj[[alg.func.obj\],window=_blank]). -[[rvariant.pack]] -== Pack manipulation and deduping [.slug]##<>## - -[,cpp,subs="+macros,+attributes"] ----- -namespace temp_ns { - -template class TT, class A, class B> -struct compact_alternative; - -} // temp_ns ----- - -[.candidates] -* [.candidate]#{empty}# Effectively concatenates contained types in `A` and `B`, then dedupes them. If the resulting type list consists of only a single type, the surrounding template is unwrapped. -+ -*_Definition:_* Let `Ts` denote an imaginary pack of types where `TT` is the same type as `<>`. The member typedef `type` denotes `Ts\...[0]` if `sizeof\...(Ts) == 1`; otherwise, the member typedef `type` denotes `TT`. - -[WARNING] -.Notes on single-type variant -A variant with a single alternative may introduce unnecessary overhead when used in many places where only the underlying type is actually needed. In such cases, the variant can be _unwrapped_ using `compact_alternative`. This is useful for resolving issues such as https://github.com/boostorg/spirit/issues/610[boostorg/spirit#610]. - -[WARNING] -`compact_alternative` does not unwrap `{recursive_wrapper}`. This is intentional, because doing so could lead to instantiating incomplete type on undesired timings. You may apply `{unwrap_recursive_type}` manually. - - -[[rvariant.xo]] -== Exposition-only utilities [.slug]##<>## -This section demonstrates internal features used in the implementation. - - -[[rvariant.xo.pack]] -=== Pack utilities [.slug]##<>## - -[,cpp,subs="+macros,+attributes"] ----- -template class TT, class A, class B> -using pass:quotes[_pack-union-t_] = {see-below}; // {exposition-only} ----- - -[.candidates] -* [.candidate]#{empty}# Let `As` denote the pack of template parameters of `A` if `A` is a specialization of `TT`, otherwise let `As` denote a pack of single type `A`. Let `Bs` denote likewise. `_pack-union-t_` denotes `TT`, where `Ts\...` is the set union of `As\...` and `Bs\...` expanded from left to right. For duplicate types, the first occurrence shall remain in `Ts\...`. - - -[[rvariant.xo.core]] -=== Core type traits [.slug]##<>## - -[,cpp,subs="+macros,+attributes"] ----- -template class TT> -struct pass:quotes[_is-ttp-specialization-of_]; // {exposition-only}pass:quotes[[.candidate\]#// 1#] - -template class TT> -struct pass:quotes[_is-nttp-specialization-of_]; // {exposition-only}pass:quotes[[.candidate\]#// 2#] - -template -struct pass:quotes[_is-specialization-of_]; // {exposition-only}pass:quotes[[.candidate\]#// 3#] ----- - -[.candidates] --- -* [.candidate]#1-2)# Inherits `std::true_type` if and only if `T` is a specialization of `TT`; otherwise, inherits `std::false_type`. - -* [.candidate]#3)# If `_any-ttp_` is a template template parameter that consists of NTTP, equivalent to `_is-nttp-specialization-of_`; otherwise, equivalent to `_is-ttp-specialization-of_`. --- - -NOTE: `_is-specialization-of_` requires {cpp}26 reflection for a straightforward resolution. For older versions, it can be worked around by a compound `requires` expression. - - = Additional Information [[visitation-technique]] @@ -1575,13 +1506,6 @@ Early `std::variant` implementations used this _function-pointer-based dispatch_ Unfortunately, GCC enables the optimization only in limited scenarios (link:https://github.com/gcc-mirror/gcc/blob/679e24f5a751663998ff7202149a749e0f7251f9/libstdc%2B%2B-v3/include/std/variant#L1863[link,window=_blank]), and LLVM has reverted it due to unresolved issues (link:https://github.com/llvm/llvm-project/issues/62648#issuecomment-1832315651[link,window=_blank]). Our benchmark results reflect this status quo, with `rvariant` performing up to about 2x faster than GCC/Clang. -[[about]] -== About the Authors - -Yaito Kakeyama is a {cpp} enthusiast with a strong interest in language design and modern library development. He has contributed to several public efforts in the {cpp} community, including co-authoring LWG 4166 with Nana Sakisaka and submitting occasional compiler bug reports. He is the co-author of `rvariant` and has been deeply involved in its implementation. - -Nana Sakisaka has taken on an active maintainer role in Boost.Spirit since May 2025. The development of `rvariant` began as part of a broader effort to modernize the Boost.Spirit.X3 codebase. He is the co-author of `rvariant` and has focused on its rationale and specification wording. - [[license]] == License This library is distributed under the https://github.com/iris-cpp/iris/blob/main/LICENSE[MIT License]. diff --git a/doc/rvariant.html b/doc/rvariant.html index 71e11eb..e7f46ac 100644 --- a/doc/rvariant.html +++ b/doc/rvariant.html @@ -874,7 +874,7 @@

rvariant<
-  int, recursive_wrapper<int>, recursive_wrapper<int, MyAllocator<int>>
+  int, recursive_wrapper<int>, recursive_wrapper_alloca<int, MyAllocator<int>>
 > v(42); // error-prone; not allowed
@@ -1507,7 +1491,10 @@

  • -

    Let VTi denote recursive_wrapper<Ti, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VTi denote Ti. Let Uj denote the jth type of the template parameter pack having the name Us on each flexibility-related functions. The corresponding alternative for rvariant is the first type for which std::is_same_v<unwrap_recursive_type<VTi>, unwrap_recursive_type<Uj>> is true.

    +

    If recursive_wrapper<Ti> or recursive_wrapper_alloca<Ti, A> (with any type A) occurs anywhere in Ts..., let VTi denote that type; otherwise, let VTi denote Ti. Let Uj denote the jth type of the template parameter pack having the name Us on each flexibility-related functions. The corresponding alternative for rvariant is the first type for which std::is_same_v<unwrap_recursive_type<VTi>, unwrap_recursive_type<Uj>> is true.

    +
  • +
  • +

    For the function that has the formal template parameter named T: if recursive_wrapper<T> or recursive_wrapper_alloca<T, A> (with any type A) occurs anywhere in Ts..., let VT denote that type; otherwise, let VT denote T.

@@ -1560,10 +1547,7 @@

Constructors
  • -

    5) Mandates: T is not a specialization of recursive_wrapper.

    -
    -

    Let VT denote recursive_wrapper<T, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VT denote T.

    -
    +

    5) Mandates: T is not a specialization of recursive_wrapper or recursive_wrapper_alloca.

    Constraints:

    @@ -1595,10 +1579,7 @@

    Constructors

  • -

    6) Mandates: T is not a specialization of recursive_wrapper.

    -
    -

    Let VT denote recursive_wrapper<T, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VT denote T.

    -
    +

    6) Mandates: T is not a specialization of recursive_wrapper or recursive_wrapper_alloca.

    Constraints:

    @@ -1931,10 +1912,7 @@

    Modifiers
    • -

      1) Let VT denote recursive_wrapper<T, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VT denote T.

      -
      -

      Mandates: T is not a specialization of recursive_wrapper.

      -
      +

      1) Mandates: T is not a specialization of recursive_wrapper or recursive_wrapper_alloca.

      Constraints: std::is_constructible_v<VT, Args...> is true, and T occurs exactly once in unwrap_recursive_type<Ts>.

      @@ -1945,10 +1923,7 @@

      Modifiers

    • -

      2) Let VT denote recursive_wrapper<T, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VT denote T.

      -
      -

      Mandates: T is not a specialization of recursive_wrapper.

      -
      +

      2) Mandates: T is not a specialization of recursive_wrapper or recursive_wrapper_alloca.

      Constraints: std::is_constructible_v<VT, std::initializer_list<U>&, Args...> is true, and T occurs exactly once in unwrap_recursive_type<Ts>.

      @@ -1964,7 +1939,7 @@

      Modifiers Returns: Let o denote a reference to the new contained value. Returns unwrap_recursive(o).

      -

      Remarks: If TI is a specialization of recursive_wrapper, this function is permitted to construct an intermediate variable tmp as if by passing std::forward<Args>(args)... to TI's constructor. Then rvariant direct-non-list-initializes the contained value of TI with the argument std::move(tmp). (Note: This allows optimization where rvariant can be assumed to become never valueless on certain cases.)

      +

      Remarks: If TI is a specialization of recursive_wrapper or recursive_wrapper_alloca, this function is permitted to construct an intermediate variable tmp as if by passing std::forward<Args>(args)... to TI's constructor. Then rvariant direct-non-list-initializes the contained value of TI with the argument std::move(tmp). (Note: This allows optimization where rvariant can be assumed to become never valueless on certain cases.)

    • @@ -1973,7 +1948,7 @@

      Modifiers Returns: Let o denote a reference to the new contained value. Returns unwrap_recursive(o).

      -

      Remarks: If TI is a specialization of recursive_wrapper, this function is permitted to construct an intermediate variable tmp as if by passing il, std::forward<Args>(args)... to TI's constructor. Then rvariant direct-non-list-initializes the contained value of TI with the argument std::move(tmp). (Note: This allows optimization where rvariant can be assumed to become never valueless on certain cases.)

      +

      Remarks: If TI is a specialization of recursive_wrapper or recursive_wrapper_alloca, this function is permitted to construct an intermediate variable tmp as if by passing il, std::forward<Args>(args)... to TI's constructor. Then rvariant direct-non-list-initializes the contained value of TI with the argument std::move(tmp). (Note: This allows optimization where rvariant can be assumed to become never valueless on certain cases.)

    @@ -2100,7 +2075,7 @@

    rva
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<std::size_t I, class T>
     struct variant_alternative; // not defined
    @@ -2111,7 +2086,7 @@ 

    rva template<std::size_t I, class... Ts> struct variant_alternative<I, rvariant<Ts...>>;// 2 -} // temp_ns

    +} // iris
    @@ -2135,7 +2110,7 @@

    Flexibility t
    -
    namespace temp_ns::rvariant_set {
    +
    namespace iris::rvariant_set {
     
     template<class W, class V>
     struct is_subset_of : std::false_type {};// 1
    @@ -2152,7 +2127,7 @@ 

    Flexibility t template<class W, class V> concept equivalent_to = subset_of<W, V> && subset_of<V, W>; -} // temp_ns::rvariant_set

    +} // iris::rvariant_set
    @@ -2182,12 +2157,12 @@

    Value access
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class T, class... Ts>
     constexpr bool holds_alternative(rvariant<Ts...> const& v) noexcept;
     
    -} // temp_ns
    +} // iris
    @@ -2198,7 +2173,7 @@

    Value access Returns: true if v.index() is equal to the zero-based index of T in unwrap_recursive_type<Ts>.

    -

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper.

    +

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper or recursive_wrapper_alloca.

  • @@ -2226,14 +2201,14 @@

    Value access Preconditions: v.index() is I.

    -

    Returns: o, where o denotes a reference to the object stored in v, if the type of the expression’s receiver is a specialization of recursive_wrapper; otherwise, returns unwrap_recursive(o).

    +

    Returns: o, where o denotes a reference to the object stored in v, if the type of the expression’s receiver is a specialization of recursive_wrapper or recursive_wrapper_alloca; otherwise, returns unwrap_recursive(o).

    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<std::size_t I, class... Ts>
     constexpr variant_alternative_t<I, rvariant<Ts...>>&
    @@ -2251,7 +2226,7 @@ 

    Value access

    +} // iris
    @@ -2266,14 +2241,14 @@

    Value access
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class T, class... Ts> constexpr T&        get(rvariant<Ts...>& v);
     template<class T, class... Ts> constexpr T&&       get(rvariant<Ts...>&& v);
     template<class T, class... Ts> constexpr T const&  get(rvariant<Ts...> const& v);
     template<class T, class... Ts> constexpr T const&& get(rvariant<Ts...> const&& v);
     
    -} // temp_ns
    +} // iris
    @@ -2284,14 +2259,14 @@

    Value access Effects: Let VT denote the type of the alternative held by v. If unwrap_recursive_type<VT> is the same type as T, returns unwrap_recursive(o), where o denotes a reference to the object stored in the rvariant. Otherwise, throws an exception of type std::bad_variant_access.

    -

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper.

    +

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper or recursive_wrapper_alloca.

    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<std::size_t I, class... Ts>
     constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>>>
    @@ -2301,7 +2276,7 @@ 

    Value access // 2 -} // temp_ns

    +} // iris
    @@ -2316,7 +2291,7 @@

    Value access
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class T, class... Ts>
     constexpr std::add_pointer_t<T>
    @@ -2326,7 +2301,7 @@ 

    Value access // 2 -} // temp_ns

    +} // iris
    @@ -2337,7 +2312,7 @@

    Value access Effects: Equivalent to: return get_if<i>(v); with i being the zero-based index of T in unwrap_recursive_type<Ts>.

    -

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper.

    +

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper or recursive_wrapper_alloca.

    @@ -2349,7 +2324,7 @@

    Visitation
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class Visitor, class... Variants>
     constexpr see below visit(Visitor&& vis, Variants&&... vars);// 1
    @@ -2357,7 +2332,7 @@ 

    Visitation template<class R, class Visitor, class... Variants> constexpr R visit(Visitor&& vis, Variants&&... vars);// 2 -} // temp_ns +} // iris // below are member functions of the class template rvariant: @@ -2381,7 +2356,7 @@

    Visitation

  • -

    3-4) Equivalent to the std::variant counterpart [spec], except that it forwards to temp_ns::visit instead of std::visit.

    +

    3-4) Equivalent to the std::variant counterpart [spec], except that it forwards to iris::visit instead of std::visit.

  • @@ -2395,25 +2370,15 @@

    Hash support
    namespace std {
     
     template<class... Ts>
    -struct hash<::temp_ns::rvariant<Ts...>>;// 1
    +struct hash<::iris::rvariant<Ts...>>;// 1
     
    -template<class T, class Allocator>
    -struct hash<::temp_ns::recursive_wrapper<T, Allocator>>;// 2
    -
    -} // std
    -

    -

    -
    -
    -
    namespace temp_ns {
    -
    -template<class... Ts>
    -/* constexpr */ std::size_t hash_value(rvariant<Ts...> const& v);// 3
    +template<class T>
    +struct hash<::iris::recursive_wrapper<T>>;// 2
     
     template<class T, class Allocator>
    -/* constexpr */ std::size_t hash_value(recursive_wrapper<T, Allocator> const& rw);// 4
    +struct hash<::iris::recursive_wrapper_alloca<T, Allocator>>;// 3
     
    -} // temp_ns
    +} // std
    @@ -2429,14 +2394,41 @@

    Hash support
    • -

      2) +

      2-3) Equivalent to the std::indirect counterpart. [spec]

    • +
    +
    +

    + +
    +
    +
    namespace iris {
    +
    +template<class... Ts>
    +/* constexpr */ std::size_t hash_value(rvariant<Ts...> const& v);// 1
    +
    +template<class T>
    +/* constexpr */ std::size_t hash_value(recursive_wrapper<T> const& rw);// 2
    +
    +template<class T, class Allocator>
    +/* constexpr */ std::size_t hash_value(recursive_wrapper_alloca<T, Allocator> const& rw);// 3
    +
    +} // iris
    +
    +
    +
    +
    +
    +
      +
    • +

      1) Effects: Equivalent to std::hash<rvariant<Ts...>>{}(v).

      +
    • -

      3) Effects: Equivalent to std::hash<rvariant<Ts...>>{}(v).

      +

      2) Effects: Equivalent to std::hash<recursive_wrapper<T>>{}(rw).

    • -

      4) Effects: Equivalent to std::hash<recursive_wrapper<T, Allocator>>{}(rw).

      +

      3) Effects: Equivalent to std::hash<recursive_wrapper_alloca<T, Allocator>>{}(rw).

    @@ -2448,17 +2440,15 @@

    Hash support

    I/O [rvariant.io]

    -

    I/O components are not included by the global convenience header (<temp_ns/rvariant.hpp>).

    +

    I/O components are not included by the global convenience header (<iris/rvariant.hpp>).

    operator<< support

    -
    // <temp_ns/rvariant/rvariant_io.hpp>
    -
    -#include <ostream>
    +
    // <iris/rvariant/rvariant_io.hpp>
     
    -namespace temp_ns {
    +namespace iris {
     
     template<class T>
     constexpr bool ADL-ostreamable = see below; // exposition only// 1
    @@ -2466,7 +2456,7 @@ 

    < template<class... Ts> std::ostream& operator<<(std::ostream& os, rvariant<Ts...> const& v);// 2 -} // temp_ns

    +} // iris
    @@ -2524,7 +2514,7 @@

    Let v denote an object of rvariant, and let proxy denote an object of variant_format_proxy.

  • -

    The specialization std::formatter<::temp_ns::rvariant<Ts...>, charT> (for arbitrary charT) is enabled if and only if std::formattable<unwrap_recursive_type<Tsi>, charT> is true for all i, with the following characteristics:

    +

    The specialization std::formatter<::iris::rvariant<Ts...>, charT> (for arbitrary charT) is enabled if and only if std::formattable<unwrap_recursive_type<Tsi>, charT> is true for all i, with the following characteristics:

    @@ -2837,35 +2827,35 @@

    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class T>
     using unwrap_recursive_type = see below;
     
    -} // temp_ns
    +} // iris
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class T>
     constexpr auto&& unwrap_recursive(T&& o) noexcept;
     
    -} // temp_ns
    +} // iris
    • -

      Returns: *o, if cv-unqualified non-reference type for T is a specialization of recursive_wrapper. Otherwise, returns o.

      +

      Returns: *o, if cv-unqualified non-reference type for T is a specialization of recursive_wrapper or recursive_wrapper_alloca. Otherwise, returns o.

      Remarks: unwrap_recursive is an algorithm function object ([alg.func.obj]).

      @@ -2874,121 +2864,6 @@

      -

      Pack manipulation and deduping [rvariant.pack]

      -
      -
      -
      -
      namespace temp_ns {
      -
      -template<template<class...> class TT, class A, class B>
      -struct compact_alternative;
      -
      -} // temp_ns
      -
      -
      -
      -
        -
      • -

        Effectively concatenates contained types in A and B, then dedupes them. If the resulting type list consists of only a single type, the surrounding template is unwrapped.

        -
        -

        Definition: Let Ts denote an imaginary pack of types where TT<Ts...> is the same type as pack-union-t<TT, A, B>. The member typedef type denotes Ts...[0] if sizeof...(Ts) == 1; otherwise, the member typedef type denotes TT<Ts...>.

        -
        -
      • -
      -
      -
      - - - - - -
      - - -
      Notes on single-type variant
      -A variant with a single alternative may introduce unnecessary overhead when used in many places where only the underlying type is actually needed. In such cases, the variant can be unwrapped using compact_alternative. This is useful for resolving issues such as boostorg/spirit#610. -
      -
      -
      - - - - - -
      - - -compact_alternative does not unwrap recursive_wrapper. This is intentional, because doing so could lead to instantiating incomplete type on undesired timings. You may apply unwrap_recursive_type manually. -
      -
      -
      -

    -
    -

    Exposition-only utilities [rvariant.xo]

    -
    -
    -

    This section demonstrates internal features used in the implementation.

    -
    -
    -

    Pack utilities [rvariant.xo.pack]

    -
    -
    -
    template<template<class...> class TT, class A, class B>
    -using pack-union-t = see below; // exposition only
    -
    -
    -
    -
      -
    • -

      Let As denote the pack of template parameters of A if A is a specialization of TT, otherwise let As denote a pack of single type A. Let Bs denote likewise. pack-union-t denotes TT<Ts...>, where Ts... is the set union of As... and Bs... expanded from left to right. For duplicate types, the first occurrence shall remain in Ts....

      -
    • -
    -
    -
    -
    -

    Core type traits [rvariant.xo.core]

    -
    -
    -
    template<class T, template<class...> class TT>
    -struct is-ttp-specialization-of;  // exposition only// 1
    -
    -template<class T, template<auto...> class TT>
    -struct is-nttp-specialization-of; // exposition only// 2
    -
    -template<class T, any-ttp>
    -struct is-specialization-of;      // exposition only// 3
    -
    -
    -
    -
    -
    -
      -
    • -

      1-2) Inherits std::true_type if and only if T is a specialization of TT; otherwise, inherits std::false_type.

      -
    • -
    • -

      3) If any-ttp is a template template parameter that consists of NTTP, equivalent to is-nttp-specialization-of; otherwise, equivalent to is-ttp-specialization-of.

      -
    • -
    -
    -
    -
    -
    - - - - - -
    - - -is-specialization-of requires C++26 reflection for a straightforward resolution. For older versions, it can be worked around by a compound requires expression. -
    -
    -
    -
    -

    Additional Information

    Visitation Technique in Depth

    @@ -3017,17 +2892,6 @@

    -

    About the Authors

    -
    -
    -

    Yaito Kakeyama is a C++ enthusiast with a strong interest in language design and modern library development. He has contributed to several public efforts in the C++ community, including co-authoring LWG 4166 with Nana Sakisaka and submitting occasional compiler bug reports. He is the co-author of rvariant and has been deeply involved in its implementation.

    -
    -
    -

    Nana Sakisaka has taken on an active maintainer role in Boost.Spirit since May 2025. The development of rvariant began as part of a broader effort to modernize the Boost.Spirit.X3 codebase. He is the co-author of rvariant and has focused on its rationale and specification wording.

    -
    -
    -
    -

    License

    diff --git a/include/iris/hash_fwd.hpp b/include/iris/hash_fwd.hpp index c0eb75b..657cc91 100644 --- a/include/iris/hash_fwd.hpp +++ b/include/iris/hash_fwd.hpp @@ -5,7 +5,7 @@ #include -#if __cplusplus <= 202302L || \ +#if !defined(_MSC_VER) && __cplusplus <= 202302L || \ (defined(_MSVC_STL_UPDATE) && _MSVC_STL_UPDATE < 202504L) || \ (defined(__GLIBCXX__) && __GLIBCXX__ < 20241201) || \ (defined(_LIBCPP_STD_VER) && _LIBCPP_STD_VER < 26) diff --git a/include/iris/indirect.hpp b/include/iris/indirect.hpp index 0d977c1..c5084cc 100644 --- a/include/iris/indirect.hpp +++ b/include/iris/indirect.hpp @@ -3,9 +3,9 @@ // SPDX-License-Identifier: MIT +#include #include #include -#include #include #include @@ -25,13 +25,12 @@ class scoped_allocation using pointer = std::allocator_traits::pointer; using const_pointer = std::allocator_traits::const_pointer; - template - constexpr explicit scoped_allocation(Alloc const& a, std::in_place_t, Args&&... args) + constexpr explicit scoped_allocation(Alloc const& a, Args&&... args) : alloc_(a) , ptr_(std::allocator_traits::allocate(alloc_, 1)) { - std::allocator_traits::construct(alloc_, get(), std::forward(args)...); + std::allocator_traits::construct(alloc_, this->get(), std::forward(args)...); } constexpr ~scoped_allocation() @@ -70,7 +69,7 @@ class indirect_base using const_pointer = std::allocator_traits::const_pointer; constexpr explicit indirect_base() requires std::is_default_constructible_v - : ptr_(make_obj()) + : ptr_(this->make_obj()) {} constexpr indirect_base(indirect_base const& other) @@ -83,12 +82,12 @@ class indirect_base constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a) : alloc_(a) - , ptr_(make_obj()) + , ptr_(this->make_obj()) {} constexpr indirect_base(std::allocator_arg_t, Allocator const& a, indirect_base const& other) : alloc_(a) - , ptr_(other.ptr_ ? make_obj(std::as_const(*other.ptr_)) : nullptr) + , ptr_(other.ptr_ ? this->make_obj(std::as_const(*other.ptr_)) : nullptr) { static_assert(std::is_copy_constructible_v); } @@ -103,7 +102,7 @@ class indirect_base : alloc_(a) , ptr_(alloc_ == other.alloc_ ? std::exchange(other.ptr_, nullptr) - : make_obj(*std::move(other)) + : this->make_obj(*std::move(other)) ) {} @@ -114,7 +113,7 @@ class indirect_base std::is_constructible_v && std::is_default_constructible_v constexpr explicit indirect_base(U&& u) - : ptr_(make_obj(std::forward(u))) + : ptr_(this->make_obj(std::forward(u))) {} template @@ -124,7 +123,7 @@ class indirect_base std::is_constructible_v constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, U&& u) : alloc_(a) - , ptr_(make_obj(std::forward(u))) + , ptr_(this->make_obj(std::forward(u))) {} template @@ -132,7 +131,7 @@ class indirect_base std::is_constructible_v && std::is_default_constructible_v constexpr explicit indirect_base(std::in_place_t, Us&&... us) - : ptr_(make_obj(std::forward(us)...)) + : ptr_(this->make_obj(std::forward(us)...)) {} template @@ -140,7 +139,7 @@ class indirect_base std::is_constructible_v constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, std::in_place_t, Us&&... us) : alloc_(a) - , ptr_(make_obj(std::forward(us)...)) + , ptr_(this->make_obj(std::forward(us)...)) {} template @@ -148,7 +147,7 @@ class indirect_base std::is_constructible_v&, Us...> && std::is_default_constructible_v constexpr explicit indirect_base(std::in_place_t, std::initializer_list il, Us&&... us) - : ptr_(make_obj(il, std::forward(us)...)) + : ptr_(this->make_obj(il, std::forward(us)...)) {} template @@ -156,13 +155,13 @@ class indirect_base std::is_constructible_v&, Us...> constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, std::in_place_t, std::initializer_list il, Us&&... us) : alloc_(a) - , ptr_(make_obj(il, std::forward(us)...)) + , ptr_(this->make_obj(il, std::forward(us)...)) {} constexpr ~indirect_base() noexcept { if (ptr_) [[likely]] { - destroy_deallocate(); + this->destroy_deallocate(); } } @@ -220,7 +219,7 @@ class indirect_base // other.ptr_ && (ptr_ || !ptr_) // either allocator is not equal or `this` does not contain value; create copy from `other` if (ptr_) [[likely]] { - destroy_deallocate(); + this->destroy_deallocate(); ptr_ = nullptr; // make it safer } if constexpr (pocca) { @@ -234,7 +233,7 @@ class indirect_base } else [[unlikely]] { // !other.ptr_ if (ptr_) [[likely]] { - destroy_deallocate(); + this->destroy_deallocate(); ptr_ = nullptr; // make it safer } if constexpr (pocca) { @@ -261,7 +260,7 @@ class indirect_base if (other.ptr_) [[likely]] { if constexpr (std::allocator_traits::is_always_equal::value) { if (ptr_) [[likely]] { - destroy_deallocate(); + this->destroy_deallocate(); } ptr_ = std::exchange(other.ptr_, nullptr); if constexpr (pocma) { @@ -271,7 +270,7 @@ class indirect_base } else if (alloc_ == other.alloc_) { if (ptr_) [[likely]] { - destroy_deallocate(); + this->destroy_deallocate(); } ptr_ = std::exchange(other.ptr_, nullptr); if constexpr (pocma) { @@ -281,7 +280,7 @@ class indirect_base } else { if (ptr_) [[likely]] { - destroy_deallocate(); + this->destroy_deallocate(); } if constexpr (pocma) { ptr_ = other.make_obj(std::move(*other)); @@ -293,7 +292,7 @@ class indirect_base } } else [[unlikely]] { // !other.ptr_ if (ptr_) [[likely]] { - destroy_deallocate(); + this->destroy_deallocate(); ptr_ = nullptr; } if constexpr (pocma) { @@ -314,7 +313,7 @@ class indirect_base **this = std::forward(u); } else [[unlikely]] { - ptr_ = make_obj(std::forward(u)); + ptr_ = this->make_obj(std::forward(u)); } return *this; } @@ -361,7 +360,7 @@ class indirect_base template [[nodiscard]] constexpr T* make_obj(Args&&... args) const { - detail::scoped_allocation sa(alloc_, std::in_place, std::forward(args)...); + detail::scoped_allocation sa(alloc_, std::forward(args)...); return sa.release(); } diff --git a/include/iris/requirements.hpp b/include/iris/requirements.hpp index ad6bfd0..724e0e4 100644 --- a/include/iris/requirements.hpp +++ b/include/iris/requirements.hpp @@ -8,7 +8,6 @@ #include #include -#include #include #include diff --git a/include/iris/rvariant/detail/recursive_traits.hpp b/include/iris/rvariant/detail/recursive_traits.hpp index a4d7c1b..b999b18 100644 --- a/include/iris/rvariant/detail/recursive_traits.hpp +++ b/include/iris/rvariant/detail/recursive_traits.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_RVARIANT_DETAIL_RECURSIVE_TRAITS_HPP +#ifndef IRIS_RVARIANT_DETAIL_RECURSIVE_TRAITS_HPP #define IRIS_RVARIANT_DETAIL_RECURSIVE_TRAITS_HPP // SPDX-License-Identifier: MIT @@ -6,7 +6,6 @@ // IWYU pragma: private, include #include -#include namespace iris::detail { @@ -20,10 +19,17 @@ struct select_maybe_wrapped_impl static constexpr std::size_t index = I; }; +template +struct select_maybe_wrapped_impl, Rest...> +{ + using type = recursive_wrapper; + static constexpr std::size_t index = I; +}; + template -struct select_maybe_wrapped_impl, Rest...> +struct select_maybe_wrapped_impl, Rest...> { - using type = recursive_wrapper; + using type = recursive_wrapper_alloca; static constexpr std::size_t index = I; }; @@ -37,7 +43,7 @@ struct select_maybe_wrapped : select_maybe_wrapped_impl { // Precondition: either T or recursive_wrapper occurs at least once in Ts... static_assert(sizeof...(Ts) > 0); - static_assert(!is_ttp_specialization_of_v); + static_assert(!detail::is_recursive_wrapper_like_v); }; template diff --git a/include/iris/rvariant/detail/rvariant_fwd.hpp b/include/iris/rvariant/detail/rvariant_fwd.hpp index ad0dc53..eae8571 100644 --- a/include/iris/rvariant/detail/rvariant_fwd.hpp +++ b/include/iris/rvariant/detail/rvariant_fwd.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_RVARIANT_DETAIL_RVARIANT_FWD_HPP +#ifndef IRIS_RVARIANT_DETAIL_RVARIANT_FWD_HPP #define IRIS_RVARIANT_DETAIL_RVARIANT_FWD_HPP // SPDX-License-Identifier: MIT @@ -20,9 +20,12 @@ namespace iris { template class rvariant; -template +template class recursive_wrapper; +template +class recursive_wrapper_alloca; + namespace detail { diff --git a/include/iris/rvariant/detail/variant_requirements.hpp b/include/iris/rvariant/detail/variant_requirements.hpp deleted file mode 100644 index 5a51241..0000000 --- a/include/iris/rvariant/detail/variant_requirements.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef IRIS_RVARIANT_DETAIL_VARIANT_REQUIREMENTS_HPP -#define IRIS_RVARIANT_DETAIL_VARIANT_REQUIREMENTS_HPP - -// SPDX-License-Identifier: MIT - -#include - -#include - -namespace iris::detail { - -template -struct check_recursive_wrapper_duplicate_impl : std::true_type {}; - -template -struct check_recursive_wrapper_duplicate_impl, T> - : std::false_type -{ - // ReSharper disable once CppStaticAssertFailure - static_assert( - false, - "rvariant cannot contain both `T` and `recursive_wrapper` " - "([rvariant.rvariant.general])." - ); -}; - -template -struct check_recursive_wrapper_duplicate_impl> - : std::false_type -{ - // ReSharper disable once CppStaticAssertFailure - static_assert( - false, - "rvariant cannot contain both `T` and `recursive_wrapper` " - "([rvariant.rvariant.general])." - ); -}; - -template - requires (!std::is_same_v) -struct check_recursive_wrapper_duplicate_impl, recursive_wrapper> - : std::false_type -{ - // ReSharper disable once CppStaticAssertFailure - static_assert( - false, - "rvariant cannot contain multiple different allocator specializations of " - "`recursive_wrapper` for the same `T` ([rvariant.rvariant.general])." - ); -}; - -template -struct check_recursive_wrapper_duplicate : std::true_type {}; - -template requires (sizeof...(Ts) > 0) -struct check_recursive_wrapper_duplicate - : std::conjunction...> -{}; - -template -struct non_wrapped_exactly_once : exactly_once -{ - static_assert( - !is_ttp_specialization_of_v, - "Constructing a `recursive_wrapper` alternative with its full type as the tag is " - "prohibited to avoid confusion; just specify `T` instead." - ); -}; - -template -constexpr bool non_wrapped_exactly_once_v = non_wrapped_exactly_once::value; - - -template -struct exactly_once_index -{ - static_assert(exactly_once_v, "T or recursive_wrapper must occur exactly once in Ts..."); - static constexpr std::size_t value = find_index_v; -}; - -template -inline constexpr std::size_t exactly_once_index_v = exactly_once_index::value; - - -template -struct variant_copy_assignable : std::conjunction, std::is_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(std::is_lvalue_reference_v); -}; - -template -struct variant_nothrow_copy_assignable : std::conjunction, std::is_nothrow_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(std::is_lvalue_reference_v); - static_assert(variant_copy_assignable::value); -}; - -template -struct variant_move_assignable : std::conjunction, std::is_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(std::is_rvalue_reference_v); -}; - -template -struct variant_nothrow_move_assignable : std::conjunction, std::is_nothrow_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(std::is_rvalue_reference_v); - static_assert(variant_move_assignable::value); -}; - -template -struct variant_assignable : std::conjunction, std::is_assignable> -{ - static_assert(!std::is_reference_v); -}; - -template -struct variant_nothrow_assignable : std::conjunction, std::is_nothrow_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(variant_assignable::value); -}; - -} // iris::detail - -#endif diff --git a/include/iris/rvariant/recursive_wrapper.hpp b/include/iris/rvariant/recursive_wrapper.hpp index f29f771..e2da0a3 100644 --- a/include/iris/rvariant/recursive_wrapper.hpp +++ b/include/iris/rvariant/recursive_wrapper.hpp @@ -3,9 +3,10 @@ // SPDX-License-Identifier: MIT +#include + #include #include -#include #include #include @@ -14,18 +15,28 @@ namespace iris { -template> +template +class recursive_wrapper; + +template +class recursive_wrapper_alloca; + +// recursive_wrapper (fixed to `std::allocator`) +// This class covers 99.99% of recursive-variant use cases. +template class recursive_wrapper - : private iris::detail::indirect_base + : private detail::indirect_base> { - static_assert(std::is_object_v); - static_assert(!std::is_array_v); - static_assert(!std::is_same_v); - static_assert(!is_ttp_specialization_of_v); - static_assert(!std::is_const_v && !std::is_volatile_v); - static_assert(std::is_same_v::value_type>); + static_assert( + !is_ttp_specialization_of_v && !is_ttp_specialization_of_v, + "recursive wrapper of recursive wrapper is not allowed" + ); + + // Note: this implementation is copied from `recursive_wrapper_alloca` below. + // If any changes are required, modify `recursive_wrapper_alloca` first. + // See https://github.com/iris-cpp/iris/issues/43 for rationale - using base_type = iris::detail::indirect_base; + using base_type = detail::indirect_base>; public: using typename base_type::allocator_type; @@ -36,25 +47,121 @@ class recursive_wrapper // Don't do this; it breaks third-party analyzer like ReSharper on MSVC //using base_type::base_type; - constexpr /* not explicit */ recursive_wrapper() - requires std::is_default_constructible_v - = default; + constexpr /* not explicit */ recursive_wrapper() = default; // Required for combination with defaulted assignment operators constexpr recursive_wrapper(recursive_wrapper const&) = default; constexpr recursive_wrapper(recursive_wrapper&&) noexcept = default; - constexpr explicit recursive_wrapper(std::allocator_arg_t, Allocator const& a) + // Converting constructor + template + requires + (!std::is_same_v, recursive_wrapper>) && + (!std::is_same_v, std::in_place_t>) && + //std::is_constructible_v // UNIMPLEMENTABLE for recursive types; instantiates infinitely + std::is_convertible_v + constexpr /* not explicit */ recursive_wrapper(U&& u) + // This overload is never `noexcept` as it always allocates. + // Note that conditionally enabling `noexcept` will lead to + // recursive instantiation in some situations. + : base_type(std::forward(u)) + {} + + template + requires + std::is_constructible_v + constexpr explicit recursive_wrapper(std::in_place_t, Us&&... us) + noexcept(noexcept(base_type(std::in_place, std::forward(us)...))) + : base_type(std::in_place, std::forward(us)...) + {} + + template + requires + std::is_constructible_v&, Us...> + constexpr explicit recursive_wrapper(std::in_place_t, std::initializer_list il, Us&&... us) + noexcept(noexcept(base_type(std::in_place, il, std::forward(us)...))) + : base_type(std::in_place, il, std::forward(us)...) + {} + + constexpr ~recursive_wrapper() = default; + + // Don't do this; it will lead to surprising result that + // MSVC attempts to instantiate move assignment operator of *rvariant* + // when a user just *defines* a struct that contains a rvariant. + // I don't know why it happens, but MSVC is certainly doing something weird + // so that it eagerly instantiates unrelated member functions. + //using base_type::operator=; + + constexpr recursive_wrapper& operator=(recursive_wrapper const&) = default; + + constexpr recursive_wrapper& operator=(recursive_wrapper&&) noexcept = default; + + // This is required for proper delegation; otherwise constructor will be called + template + requires + (!std::is_same_v, recursive_wrapper>) && + std::is_constructible_v && + std::is_assignable_v + constexpr recursive_wrapper& operator=(U&& u) + { + base_type::operator=(std::forward(u)); + return *this; + } + + using base_type::operator*; + using base_type::operator->; + using base_type::valueless_after_move; + using base_type::get_allocator; + + using base_type::swap; + + friend constexpr void swap(recursive_wrapper& lhs, recursive_wrapper& rhs) + noexcept(noexcept(lhs.swap(rhs))) + { + lhs.swap(rhs); + } +}; + +// "Allocator-aware" recursive_wrapper +template> +class recursive_wrapper_alloca + : private detail::indirect_base +{ + static_assert( + !is_ttp_specialization_of_v && !is_ttp_specialization_of_v, + "recursive wrapper of recursive wrapper is not allowed" + ); + + using base_type = detail::indirect_base; + +public: + using typename base_type::allocator_type; + using typename base_type::const_pointer; + using typename base_type::pointer; + using typename base_type::value_type; + + // Don't do this; it breaks third-party analyzer like ReSharper on MSVC + //using base_type::base_type; + + constexpr /* not explicit */ recursive_wrapper_alloca() + requires std::is_default_constructible_v + = default; + + // Required for combination with defaulted assignment operators + constexpr recursive_wrapper_alloca(recursive_wrapper_alloca const&) = default; + constexpr recursive_wrapper_alloca(recursive_wrapper_alloca&&) noexcept = default; + + constexpr explicit recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a) noexcept(noexcept(base_type(std::allocator_arg, a))) : base_type(std::allocator_arg, a) {} - constexpr recursive_wrapper(std::allocator_arg_t, Allocator const& a, recursive_wrapper const& other) + constexpr recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, recursive_wrapper_alloca const& other) noexcept(noexcept(base_type(std::allocator_arg, a, other))) : base_type(std::allocator_arg, a, other) {} - constexpr recursive_wrapper(std::allocator_arg_t, Allocator const& a, recursive_wrapper&& other) + constexpr recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, recursive_wrapper_alloca&& other) noexcept(noexcept(base_type(std::allocator_arg, a, std::move(other)))) : base_type(std::allocator_arg, a, std::move(other)) {} @@ -62,12 +169,12 @@ class recursive_wrapper // Converting constructor template requires - (!std::is_same_v, recursive_wrapper>) && + (!std::is_same_v, recursive_wrapper_alloca>) && (!std::is_same_v, std::in_place_t>) && std::is_default_constructible_v && //std::is_constructible_v // UNIMPLEMENTABLE for recursive types; instantiates infinitely std::is_convertible_v - constexpr /* not explicit */ recursive_wrapper(U&& u) + constexpr /* not explicit */ recursive_wrapper_alloca(U&& u) // This overload is never `noexcept` as it always allocates. // Note that conditionally enabling `noexcept` will lead to // recursive instantiation in some situations. @@ -76,10 +183,10 @@ class recursive_wrapper template requires - (!std::is_same_v, recursive_wrapper>) && + (!std::is_same_v, recursive_wrapper_alloca>) && (!std::is_same_v, std::in_place_t>) && std::is_constructible_v - constexpr explicit recursive_wrapper(std::allocator_arg_t, Allocator const& a, U&& u) + constexpr explicit recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, U&& u) noexcept(noexcept(base_type(std::allocator_arg, a, std::forward(u)))) : base_type(std::allocator_arg, a, std::forward(u)) {} @@ -88,14 +195,14 @@ class recursive_wrapper requires std::is_constructible_v && std::is_default_constructible_v - constexpr explicit recursive_wrapper(std::in_place_t, Us&&... us) + constexpr explicit recursive_wrapper_alloca(std::in_place_t, Us&&... us) noexcept(noexcept(base_type(std::in_place, std::forward(us)...))) : base_type(std::in_place, std::forward(us)...) {} template requires std::is_constructible_v - constexpr explicit recursive_wrapper(std::allocator_arg_t, Allocator const& a, std::in_place_t, Us&&... us) + constexpr explicit recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, std::in_place_t, Us&&... us) noexcept(noexcept(base_type(std::allocator_arg, a, std::in_place, std::forward(us)...))) : base_type(std::allocator_arg, a, std::in_place, std::forward(us)...) {} @@ -104,19 +211,19 @@ class recursive_wrapper requires std::is_constructible_v&, Us...> && std::is_default_constructible_v - constexpr explicit recursive_wrapper(std::in_place_t, std::initializer_list il, Us&&... us) + constexpr explicit recursive_wrapper_alloca(std::in_place_t, std::initializer_list il, Us&&... us) noexcept(noexcept(base_type(std::in_place, il, std::forward(us)...))) : base_type(std::in_place, il, std::forward(us)...) {} template requires std::is_constructible_v&, Us...> - constexpr explicit recursive_wrapper(std::allocator_arg_t, Allocator const& a, std::in_place_t, std::initializer_list il, Us&&... us) + constexpr explicit recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, std::in_place_t, std::initializer_list il, Us&&... us) noexcept(noexcept(base_type(std::allocator_arg, a, std::in_place, il, std::forward(us)...))) : base_type(std::allocator_arg, a, std::in_place, il, std::forward(us)...) {} - constexpr ~recursive_wrapper() = default; + constexpr ~recursive_wrapper_alloca() = default; // Don't do this; it will lead to surprising result that // MSVC attempts to instantiate move assignment operator of *rvariant* @@ -125,9 +232,9 @@ class recursive_wrapper // so that it eagerly instantiates unrelated member functions. //using base_type::operator=; - constexpr recursive_wrapper& operator=(recursive_wrapper const&) = default; + constexpr recursive_wrapper_alloca& operator=(recursive_wrapper_alloca const&) = default; - constexpr recursive_wrapper& operator=(recursive_wrapper&&) noexcept( + constexpr recursive_wrapper_alloca& operator=(recursive_wrapper_alloca&&) noexcept( std::allocator_traits::propagate_on_container_move_assignment::value || std::allocator_traits::is_always_equal::value ) = default; @@ -135,10 +242,10 @@ class recursive_wrapper // This is required for proper delegation; otherwise constructor will be called template requires - (!std::is_same_v, recursive_wrapper>) && + (!std::is_same_v, recursive_wrapper_alloca>) && std::is_constructible_v && std::is_assignable_v - constexpr recursive_wrapper& operator=(U&& u) + constexpr recursive_wrapper_alloca& operator=(U&& u) { base_type::operator=(std::forward(u)); return *this; @@ -151,23 +258,55 @@ class recursive_wrapper using base_type::swap; - friend constexpr void swap(recursive_wrapper& lhs, recursive_wrapper& rhs) + friend constexpr void swap(recursive_wrapper_alloca& lhs, recursive_wrapper_alloca& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } }; +// --------------------------------------------------- +// recursive_wrapper + template recursive_wrapper(Value) -> recursive_wrapper; +template +[[nodiscard]] constexpr bool operator==(recursive_wrapper const& lhs, recursive_wrapper const& rhs) + noexcept(noexcept(*lhs == *rhs)) +{ + if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] { + return lhs.valueless_after_move() == rhs.valueless_after_move(); + } else [[likely]] { + return *lhs == *rhs; + } +} + +template +[[nodiscard]] constexpr bool operator==(recursive_wrapper const& lhs, U const& rhs) + noexcept(noexcept(*lhs == rhs)) +{ + if (lhs.valueless_after_move()) [[unlikely]] { + return false; + } else [[likely]] { + return *lhs == rhs; + } +} + +// --------------------------------------------------- +// recursive_wrapper_alloca + +template +recursive_wrapper_alloca(Value) + -> recursive_wrapper_alloca; + template -recursive_wrapper(std::allocator_arg_t, Allocator, Value) - -> recursive_wrapper::template rebind_alloc>; +recursive_wrapper_alloca(std::allocator_arg_t, Allocator, Value) + -> recursive_wrapper_alloca::template rebind_alloc>; template -[[nodiscard]] constexpr bool operator==(recursive_wrapper const& lhs, recursive_wrapper const& rhs) +[[nodiscard]] constexpr bool operator==(recursive_wrapper_alloca const& lhs, recursive_wrapper_alloca const& rhs) noexcept(noexcept(*lhs == *rhs)) { if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] { @@ -178,7 +317,7 @@ template } template -[[nodiscard]] constexpr bool operator==(recursive_wrapper const& lhs, U const& rhs) +[[nodiscard]] constexpr bool operator==(recursive_wrapper_alloca const& lhs, U const& rhs) noexcept(noexcept(*lhs == rhs)) { if (lhs.valueless_after_move()) [[unlikely]] { @@ -193,8 +332,36 @@ namespace detail { // These cannot be overloaded with the same function name, as it // breaks MSVC's overload resolution on recursive types (possibly bug) +// --------------------------------------------------- +// recursive_wrapper + +template +[[nodiscard]] constexpr auto rw_three_way_impl_00(recursive_wrapper const& lhs, recursive_wrapper const& rhs) + -> cmp::synth_three_way_result +{ + if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] { + return !lhs.valueless_after_move() <=> !rhs.valueless_after_move(); + } else [[likely]] { + return cmp::synth_three_way{}(*lhs, *rhs); + } +} + +template +[[nodiscard]] constexpr auto rw_three_way_impl_01(recursive_wrapper const& lhs, U const& rhs) + -> cmp::synth_three_way_result +{ + if (lhs.valueless_after_move()) [[unlikely]] { + return std::strong_ordering::less; + } else [[likely]] { + return cmp::synth_three_way{}(*lhs, rhs); + } +} + +// --------------------------------------------------- +// recursive_wrapper_alloca + template -[[nodiscard]] constexpr auto rw_three_way_impl_00(recursive_wrapper const& lhs, recursive_wrapper const& rhs) +[[nodiscard]] constexpr auto rw_three_way_impl_00(recursive_wrapper_alloca const& lhs, recursive_wrapper_alloca const& rhs) -> cmp::synth_three_way_result { if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] { @@ -205,7 +372,7 @@ template } template -[[nodiscard]] constexpr auto rw_three_way_impl_01(recursive_wrapper const& lhs, U const& rhs) +[[nodiscard]] constexpr auto rw_three_way_impl_01(recursive_wrapper_alloca const& lhs, U const& rhs) -> cmp::synth_three_way_result { if (lhs.valueless_after_move()) [[unlikely]] { @@ -217,15 +384,36 @@ template } // detail + +// --------------------------------------------------- +// recursive_wrapper + +template +[[nodiscard]] constexpr auto operator<=>(recursive_wrapper const& lhs, recursive_wrapper const& rhs) + // no explicit return type +{ + return detail::rw_three_way_impl_00(lhs, rhs); +} + +template +[[nodiscard]] constexpr auto operator<=>(recursive_wrapper const& lhs, U const& rhs) + // no explicit return type +{ + return detail::rw_three_way_impl_01(lhs, rhs); +} + +// --------------------------------------------------- +// recursive_wrapper_alloca + template -[[nodiscard]] constexpr auto operator<=>(recursive_wrapper const& lhs, recursive_wrapper const& rhs) +[[nodiscard]] constexpr auto operator<=>(recursive_wrapper_alloca const& lhs, recursive_wrapper_alloca const& rhs) // no explicit return type { return detail::rw_three_way_impl_00(lhs, rhs); } template -[[nodiscard]] constexpr auto operator<=>(recursive_wrapper const& lhs, U const& rhs) +[[nodiscard]] constexpr auto operator<=>(recursive_wrapper_alloca const& lhs, U const& rhs) // no explicit return type { return detail::rw_three_way_impl_01(lhs, rhs); @@ -235,11 +423,32 @@ template namespace std { +// --------------------------------------------------- +// recursive_wrapper + +template + requires ::iris::is_hash_enabled_v +struct hash<::iris::recursive_wrapper> // NOLINT(cert-dcl58-cpp) +{ + [[nodiscard]] static size_t operator()(::iris::recursive_wrapper const& obj) + noexcept(::iris::is_nothrow_hashable_v) + { + if (obj.valueless_after_move()) [[unlikely]] { + return 0xbaddeadbeefuz; + } else [[likely]] { + return std::hash{}(*obj); + } + } +}; + +// --------------------------------------------------- +// recursive_wrapper_alloca + template requires ::iris::is_hash_enabled_v -struct hash<::iris::recursive_wrapper> // NOLINT(cert-dcl58-cpp) +struct hash<::iris::recursive_wrapper_alloca> // NOLINT(cert-dcl58-cpp) { - [[nodiscard]] static size_t operator()(::iris::recursive_wrapper const& obj) + [[nodiscard]] static size_t operator()(::iris::recursive_wrapper_alloca const& obj) noexcept(::iris::is_nothrow_hashable_v) { if (obj.valueless_after_move()) [[unlikely]] { @@ -255,12 +464,26 @@ struct hash<::iris::recursive_wrapper> // NOLINT(cert-dcl58-cpp) namespace iris { +// --------------------------------------------------- +// recursive_wrapper + +template + requires is_hash_enabled_v +[[nodiscard]] std::size_t hash_value(recursive_wrapper const& obj) + noexcept(is_nothrow_hashable_v) +{ + return std::hash>{}(obj); +} + +// --------------------------------------------------- +// recursive_wrapper_alloca + template requires is_hash_enabled_v -[[nodiscard]] std::size_t hash_value(recursive_wrapper const& obj) +[[nodiscard]] std::size_t hash_value(recursive_wrapper_alloca const& obj) noexcept(is_nothrow_hashable_v) { - return std::hash>{}(obj); + return std::hash>{}(obj); } } // iris diff --git a/include/iris/rvariant/recursive_wrapper_pmr.hpp b/include/iris/rvariant/recursive_wrapper_pmr.hpp index 0e5e020..853fd58 100644 --- a/include/iris/rvariant/recursive_wrapper_pmr.hpp +++ b/include/iris/rvariant/recursive_wrapper_pmr.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_RVARIANT_RECURSIVE_WRAPPER_PMR_HPP +#ifndef IRIS_RVARIANT_RECURSIVE_WRAPPER_PMR_HPP #define IRIS_RVARIANT_RECURSIVE_WRAPPER_PMR_HPP // SPDX-License-Identifier: MIT @@ -10,7 +10,7 @@ namespace iris::pmr { template -using recursive_wrapper = iris::recursive_wrapper>; +using recursive_wrapper = iris::recursive_wrapper_alloca>; } // iris::pmr diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index 19f0a95..399c7bd 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -4,7 +4,6 @@ // SPDX-License-Identifier: MIT #include -#include #include #include #include @@ -18,6 +17,7 @@ #include #include +#include #include #include #include @@ -32,6 +32,102 @@ namespace iris { namespace detail { +template +struct check_recursive_wrapper_duplicate_impl : std::true_type {}; + +template + requires + (!std::same_as) && + (is_recursive_wrapper_like_v || is_recursive_wrapper_like_v) && + std::same_as, unwrap_recursive_type> +struct check_recursive_wrapper_duplicate_impl + : std::false_type +{ + // ReSharper disable once CppStaticAssertFailure + static_assert( + false, + "rvariant cannot contain both `T` and `recursive_wrapper` of `T` " + "([rvariant.rvariant.general])." + ); +}; + +template +struct check_recursive_wrapper_duplicate : std::true_type {}; + +template requires (sizeof...(Ts) > 0) +struct check_recursive_wrapper_duplicate + : std::conjunction...> +{}; + +template +struct non_wrapped_exactly_once : exactly_once +{ + static_assert( + !detail::is_recursive_wrapper_like_v, + "Constructing a `recursive_wrapper` alternative with its full type as the tag is " + "prohibited to avoid confusion; just specify `T` instead." + ); +}; + +template +constexpr bool non_wrapped_exactly_once_v = non_wrapped_exactly_once::value; + + +template +struct exactly_once_index +{ + static_assert(exactly_once_v, "`T` or `recursive_wrapper` or `recursive_wrapper_alloca` must occur exactly once in Ts..."); + static constexpr std::size_t value = find_index_v; +}; + +template +inline constexpr std::size_t exactly_once_index_v = exactly_once_index::value; + + +template +struct variant_copy_assignable : std::conjunction, std::is_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(std::is_lvalue_reference_v); +}; + +template +struct variant_nothrow_copy_assignable : std::conjunction, std::is_nothrow_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(std::is_lvalue_reference_v); + static_assert(variant_copy_assignable::value); +}; + +template +struct variant_move_assignable : std::conjunction, std::is_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(std::is_rvalue_reference_v); +}; + +template +struct variant_nothrow_move_assignable : std::conjunction, std::is_nothrow_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(std::is_rvalue_reference_v); + static_assert(variant_move_assignable::value); +}; + +template +struct variant_assignable : std::conjunction, std::is_assignable> +{ + static_assert(!std::is_reference_v); +}; + +template +struct variant_nothrow_assignable : std::conjunction, std::is_nothrow_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(variant_assignable::value); +}; + + template struct relops_visitor; @@ -371,7 +467,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN } else if constexpr (std::is_same_v) { // NOT type-changing if constexpr ( (sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_move_assignable_v) || - is_ttp_specialization_of_v + detail::is_recursive_wrapper_like_v ) { T tmp{std::forward(args)...}; // may throw static_assert(noexcept(t_old_i = std::move(tmp))); @@ -394,7 +490,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN } else { // type-changing if constexpr ( (sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_move_constructible_v) || - is_ttp_specialization_of_v + detail::is_recursive_wrapper_like_v ) { T tmp{std::forward(args)...}; // may throw t_old_i.~T_old_i(); @@ -598,7 +694,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; using VT = maybe_wrapped::type; static_assert(std::is_same_v, unwrap_recursive_type>); - base_type::template construct_on_valueless(detail::forward_maybe_wrapped(uj)); + base_type::template construct_on_valueless(uj); } }); } @@ -621,7 +717,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END using VT = maybe_wrapped::type; static_assert(std::is_same_v, unwrap_recursive_type>); static_assert(std::is_rvalue_reference_v); - base_type::template construct_on_valueless(detail::forward_maybe_wrapped(std::move(uj))); // NOLINT(bugprone-move-forwarding-reference) + base_type::template construct_on_valueless(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) } }); } @@ -654,16 +750,16 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END { constexpr std::size_t VTi = maybe_wrapped::index; if constexpr (i == std::variant_npos) { // this is valueless, rhs holds value - base_type::template construct_on_valueless(detail::forward_maybe_wrapped(uj)); + base_type::template construct_on_valueless(uj); } else if constexpr (std::is_same_v, unwrap_recursive_type>) { - ti = detail::forward_maybe_wrapped(uj); + ti = uj; } else if constexpr (std::is_nothrow_constructible_v || !std::is_nothrow_move_constructible_v) { - base_type::template reset_construct(detail::forward_maybe_wrapped(uj)); + base_type::template reset_construct(uj); } else { - VT tmp = detail::forward_maybe_wrapped(uj); // may throw + VT tmp(uj); // may throw base_type::template reset_construct(std::move(tmp)); } }); @@ -699,13 +795,13 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END static_assert(std::is_rvalue_reference_v); constexpr std::size_t VTi = maybe_wrapped::index; if constexpr (i == std::variant_npos) { // this is valueless, rhs holds value - base_type::template construct_on_valueless(detail::forward_maybe_wrapped(std::move(uj))); // NOLINT(bugprone-move-forwarding-reference) + base_type::template construct_on_valueless(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) } else if constexpr (std::is_same_v, unwrap_recursive_type>) { - ti = detail::forward_maybe_wrapped(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) + ti = std::move(uj); // NOLINT(bugprone-move-forwarding-reference) } else { - base_type::template reset_construct(detail::forward_maybe_wrapped(std::move(uj))); // NOLINT(bugprone-move-forwarding-reference) + base_type::template reset_construct(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) } }); } @@ -1095,7 +1191,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END // ------------------------------------------------- template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v [[nodiscard]] constexpr bool holds_alternative(rvariant const& v) noexcept = delete; template @@ -1196,19 +1292,19 @@ get(rvariant const&& v IRIS_LIFETIMEBOUND) } template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T& get(rvariant&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T&& get(rvariant&&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T const& get(rvariant const&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T const&& get(rvariant const&&) = delete; // ------------------------------------------------- @@ -1277,19 +1373,19 @@ unsafe_get(rvariant const&& v IRIS_LIFETIMEBOUND) noexcept } template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T& unsafe_get(rvariant&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T&& unsafe_get(rvariant&&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T const& unsafe_get(rvariant const&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T const&& unsafe_get(rvariant const&&) = delete; // --------------------------------------------- diff --git a/include/iris/rvariant/variant_helper.hpp b/include/iris/rvariant/variant_helper.hpp index 840dc51..006ab45 100644 --- a/include/iris/rvariant/variant_helper.hpp +++ b/include/iris/rvariant/variant_helper.hpp @@ -69,14 +69,25 @@ struct variant_size> : std::integral_constant +constexpr bool is_recursive_wrapper_like_v = + is_ttp_specialization_of_v || + is_ttp_specialization_of_v; + template struct unwrap_recursive_type_impl { using type = T; }; +template +struct unwrap_recursive_type_impl> +{ + using type = T; +}; + template -struct unwrap_recursive_type_impl> +struct unwrap_recursive_type_impl> { using type = T; }; @@ -87,7 +98,7 @@ struct unwrap_recursive_fn [[nodiscard]] IRIS_FORCEINLINE static constexpr auto&& operator()(T&& o IRIS_LIFETIMEBOUND) noexcept { - if constexpr (is_ttp_specialization_of_v, recursive_wrapper>) { + if constexpr (is_recursive_wrapper_like_v>) { return *std::forward(o); } else { return std::forward(o); @@ -125,57 +136,6 @@ struct overloaded : Fs... using Fs::operator()...; }; - -namespace detail { - -template -struct forward_maybe_wrapped_impl; // [rvariant.rvariant.general]: different allocators are not allowed - -// recursive_wrapper val = recursive_wrapper{42}; -template -struct forward_maybe_wrapped_impl, recursive_wrapper> -{ - template - [[nodiscard]] static constexpr auto&& apply(Wrapped&& wrapped IRIS_LIFETIMEBOUND) noexcept - { - static_assert(std::is_same_v, recursive_wrapper>); - return std::forward(wrapped); - } -}; - -// recursive_wrapper val = 42; -template -struct forward_maybe_wrapped_impl, T> -{ - template - [[nodiscard]] static constexpr auto&& apply(Value&& value IRIS_LIFETIMEBOUND) noexcept - { - static_assert(std::is_same_v, T>); - return std::forward(value); - } -}; - -// int val = 42; -template -struct forward_maybe_wrapped_impl -{ - template - [[nodiscard]] static constexpr auto&& apply(Value&& value IRIS_LIFETIMEBOUND) noexcept - { - static_assert(std::is_same_v, T>); - return std::forward(value); - } -}; - -template -[[nodiscard]] constexpr auto&& forward_maybe_wrapped(RHS&& rhs IRIS_LIFETIMEBOUND) noexcept -{ - static_assert(!std::is_reference_v && !std::is_const_v); - return forward_maybe_wrapped_impl>::apply(std::forward(rhs)); -} - -} // detail - } // iris #endif diff --git a/test/rvariant/recursive_wrapper.cpp b/test/rvariant/recursive_wrapper.cpp index b3cfb76..4b8506c 100644 --- a/test/rvariant/recursive_wrapper.cpp +++ b/test/rvariant/recursive_wrapper.cpp @@ -30,7 +30,7 @@ TEST_CASE("relational operators", "[wrapper]") CHECK((a <=> b) == std::strong_ordering::greater); } { - iris::recursive_wrapper a(33); + iris::recursive_wrapper a(33); int b = 4; CHECK(a == a); @@ -62,8 +62,8 @@ TEST_CASE("relational operators", "[wrapper]") { struct MyAllocator : std::allocator {}; - iris::recursive_wrapper a(33); - iris::recursive_wrapper b(4); + iris::recursive_wrapper_alloca a(33); + iris::recursive_wrapper_alloca b(4); CHECK(a == a); CHECK(a != b); diff --git a/test/rvariant/rvariant.cpp b/test/rvariant/rvariant.cpp index 6f9118c..952ed0b 100644 --- a/test/rvariant/rvariant.cpp +++ b/test/rvariant/rvariant.cpp @@ -1597,7 +1597,7 @@ TEST_CASE("recursive_wrapper") // not [recursive] iris::rvariant> b(std::in_place_index<0>, 42); iris::rvariant> c(std::in_place_index<0>, std::in_place); iris::rvariant> d(std::in_place_index<0>, std::in_place, 42); - iris::rvariant> e(std::in_place_index<0>, std::allocator_arg, std::allocator{}); + iris::rvariant> e(std::in_place_index<0>, std::allocator_arg, std::allocator{}); } }
  • rvariantReference [rvariant] @@ -1496,7 +1480,7 @@