rvariant
is a variant library that supports recursive types. It is API-compatible with std::variant
and includes Boost-style interfaces that act as proxies to the standard-compatible API.
// A common pattern for representing recursive ASTs using recursive variants.
struct BinaryExpr;
using Expr = temp_ns::rvariant<int, double, temp_ns::recursive_wrapper<BinaryExpr>>;
enum class Op;
struct BinaryExpr { Expr lhs, rhs; Op op{}; };
Expr expr{BinaryExpr{Expr{42}, Expr{3.14}}};
expr.visit(temp_ns::overloaded{
[](int const&) { /* ... */ },
[](double const&) { /* ... */ },
[](BinaryExpr const&) { /* ... */ },
});
This library is intended for future submission to the Boost Libraries. Until then, the namespace is documented as temp_ns to indicate that the name is not yet stable. The current implementation temporarily uses the namespace yk , which originates from the prototype codebase.
|
Introduction
Motivation
Since its introduction in 2004, boost::variant
has been used for a wide range of type-safe union use cases.
Starting with C++17, most of those can be replaced with std::variant
, except for recursive types. As a result, many users of generic frameworks that require recursive variants—most notably Boost.Spirit—have continued using boost::variant
, despite its significant impact on compile times.
The compile-time slowness of boost::variant
stems from long-standing technical debt: it relies heavily on preprocessor magic in Boost.MPL. This wizardry is so tightly coupled with boost::variant
's internals that any attempt to modernize it would be unrealistic; it would effectively require a complete rewrite.
Until 2025, no one had managed to introduce a modern alternative into either Boost or the C++ standard. rvariant
fills this gap with a new implementation that supports recursive types while remaining API-compatible with std::variant
.
Project Goals
-
Provide a modern, efficient, and battle-tested recursive variant library to enable developers to avoid using Boost.Variant in new applications.
-
Replace existing usages of Boost.Variant in established frameworks, especially Boost.Spirit.X3.
-
Explore potential directions for future standardization, while gaining implementation experience with the modernized design.
Comparison of Variant Libraries
|
||||
---|---|---|---|---|
Minimum C++ version |
C++23 |
C++17 |
C++11 |
C++11 |
Compilation speed |
Average |
Average |
Very Slow |
Average |
Interface for recursive types |
Yes |
No |
No |
|
May be valueless? |
Yes |
|||
Exception safety |
Basic exception safety |
Basic exception safety |
Strong exception safety |
Strong exception safety |
Flexible construction |
Yes |
No |
Yes |
Yes |
Rationale
Why is recursive_wrapper needed?
A recursive type is effectively an incomplete type at the point of its initial definition. However, a std
-style variant class requires all alternative types to be complete, since the storage size must be determined at compile time. As a result, recursive alternatives must be wrapped in a recursive wrapper class, which holds the incomplete type via a pointer and manages it through dynamic memory allocation.
Why can’t I just use std::unique_ptr instead of recursive_wrapper?
Theoretically, a recursive wrapper class can be designed independently, i.e. having no correlation to the variant class itself. So there’s no theoretical reason to prevent you from using std::unique_ptr
with std::variant
.
However, it is essential to have compatible interfaces in the variant class, because the lack of interfaces means that you must:
-
wrap the actual type with the wrapper class every time you modify variants, and
-
unwrap the instance every time you access an alternative with
get()
orvisit()
.
In other words, the commonly seen advice like "use std::unique_ptr if you want to hold recursive types in std::variant" is a hack that basically does nothing except for just holding the wrapper instance. As said above, such unadapted wrappers introduce intrusive boilerplates that spreads to the application layer, which is undesirable in practice.
Why can’t rvariant be a thin wrapper around std::variant?
rvariant
is designed to be a strict superset of std::variant
, not just to act as a third-party drop-in replacement.
If rvariant
is specified correctly, it has the potential to become the only variant library in the C++ standard capable of handling both recursive and non-recursive types transparently. However, if it is designed merely as a thin wrapper around std::variant
(e.g. inheritance or composition), such a specification could never be standardized.
For this purpose, the documentation of rvariant
is presented in a diff-based format that mirrors std::variant
, with key characteristics marked using underlines.
Why is rvariant being proposed to Boost?
One of the co-authors of rvariant
, Nana Sakisaka, has been using Boost.Spirit for over 15 years and was endorsed as its maintainer in May 2025. Modernizing recursive variant support is a fundamental part of upgrading the Boost.Spirit.X3 codebase, and has become a main focus of ongoing maintenance efforts.
Although Boost.Spirit.X3 has a somewhat updated counterpart, it was never designed around a formal specification and still retains legacy implementation details from earlier stages of development.
As such, the initial version of rvariant
was developed as an internal replacement within Boost.Spirit, as a sub-component with no external exposure. However, before a PR was even submitted, it became clear that the design had potential value beyond Spirit. This prompted a complete redesign into a standalone superset of std::variant
, followed by preparation for formal Boost review.
Why can’t rvariant be submitted directly to the C++ Standards Committee?
Historically, the concept of recursive variants in C++ has been closely tied to Boost.Variant and Boost.Spirit.Qi. It has been investigated, implemented, and used across Boost Libraries and by end users for decades. For this reason, we believe it’s best to first gather feedback from the Boost community.
Moreover, we expect the working group to seek implementation experience, which inevitably involves the current state of Boost.Variant and related components. Submitting rvariant
independently would likely result in making the process unnecessarily time-consuming.
Are recursive variants useful in general-purpose code?
Boost.Spirit is just one example of a parser combinator library. In practice, any application that constructs statically typed recursive data structures can benefit from a recursive variant class. Furthermore, with the arrival of C++26 reflection, such data structures are likely to become even more common.
A proper solution to these challenges requires a fully-featured library, not just a value-semantic wrapper that happens to work for minimal use cases. We hope this work can serve as a foundation for broader discussion about recursive variants in Boost and beyond.
Feature Cheat Sheet
This section shows the pseudo-code for all features in this library. You can click on the links to jump to the corresponding reference.
Basic Features
using A = int;
using B = double;
struct C {};
using AB = temp_ns::rvariant<A, B>;
using BA = temp_ns::rvariant<B, A>;
using ABC = temp_ns::rvariant<A, B, C>;
// constructor
AB ab{42};
{
AB local_ab{};
} // destructor
ab.emplace<int>(123);
ab.emplace<0>(123);
// assignment
ab = AB{123};
A& a = temp_ns::get<0>(ab);
A& a = temp_ns::get<A>(ab);
C& c = temp_ns::get<C>(ab); // throws std::bad_variant_access
A* a = temp_ns::get_if<0>(&ab);
A* a = temp_ns::get_if<A>(&ab);
C* c = temp_ns::get_if<C>(&ab); // nullptr
// compatibility with boost; same effect as get_if
A* a = temp_ns::get<0>(&ab);
A* a = temp_ns::get<A>(&ab);
C* c = temp_ns::get<C>(&ab); // nullptr
ab == ab;
ab < ab;
ab <=> ab; // requires (std::three_way_comparable<Ts> && ...)
auto visitor = temp_ns::overloaded {
[](A const& a) {},
[](B const& b) {},
};
ab.visit(visitor); // member visit
temp_ns::visit(visitor, ab); // function visit
Advanced Features
{
// flexible construction
AB ab{BA{}}; // unordered construction
ABC abc{AB{}}; // subset construction
// flexible assignment
ab = BA{}; // unordered assignment
abc = AB{}; // subset assignment
}
static_assert(temp_ns::variant_size_v<AB> == 2);
static_assert(std::same_as<temp_ns::variant_alternative_t<0, AB>, A>);
static_assert(temp_ns::holds_alternative<A>(ab));
static_assert(!ab.valueless_by_exception());
static_assert(ab.index() != std::variant_npos);
{
AB tmp;
ab.swap(tmp);
}
{
using std::swap;
AB tmp;
swap(ab, tmp); // ADL
}
std::size_t _ = std::hash<AB>{ab}();
std::size_t _ = hash_value(ab); // compatibility with boost
// I/O support
{
using V = ::temp_ns::rvariant<int, double>;
// operator<<
support
std::cout << V{42} << '\n'; // prints 42
// std::formatter
support
std::println("{}", V{42}); // prints 42
constexpr auto v_fmt = temp_ns::variant_format_for<V>("{:04d}", "{:.1f}");
std::println("foo{}bar", temp_ns::format_by(v_fmt, V(42)); // prints foo0042bar
std::println("foo{}bar", temp_ns::format_by(v_fmt, V(3.14)); // prints foo3.1bar
}
Reference [rvariant]
General [rvariant.general]
-
In [rvariant],
GET
denotes a set of exposition-only function templates ([rvariant.get]).
Header <temp_ns/rvariant.hpp> synopsis [rvariant.syn]
#include <compare>
#include <memory> // for std::allocator
, etc.
#include <type_traits> // for std::add_pointer
, etc.
#include <utility> // for std::in_place_type
, etc.
#include <variant> // for compatibility with std::bad_variant_access
, etc.
namespace temp_ns {
// [rvariant.rvariant], class template rvariant
template<class... Ts>
class rvariant;
// [rvariant.recursive], class template recursive_wrapper
template<class T, class Allocator = std::allocator<T>>
class recursive_wrapper;
/* all features commented below defined as per [variant] */
// variant_size, variant_size_v
// operator==
// operator!=
// operator<
// operator>
// operator<=
// operator>=
// operator<=>
// swap
/* not defined; use the std::
versions instead */
// variant_npos
// monostate and monostate-related functionalities
// std::bad_variant_access
// [rvariant.helper], rvariant
helper classes
template<std::size_t I, class T> struct variant_alternative; // not defined
template<std::size_t I, class T> struct variant_alternative<I, T const>;
template<std::size_t I, class T>
using variant_alternative_t = typename variant_alternative<I, T>::type;
template<std::size_t I, class... Ts>
struct variant_alternative<I, rvariant<Ts...>>;
// [rvariant.get], value access
template<class T, class... Ts>
constexpr bool holds_alternative(rvariant<Ts...> const&) noexcept;
template<std::size_t I, class... Ts>
constexpr variant_alternative_t<I, rvariant<Ts...>>&
get(rvariant<Ts...>&);
template<std::size_t I, class... Ts>
constexpr variant_alternative_t<I, rvariant<Ts...>>&&
get(rvariant<Ts...>&&);
template<std::size_t I, class... Ts>
constexpr variant_alternative_t<I, rvariant<Ts...>> const&
get(rvariant<Ts...> const&);
template<std::size_t I, class... Ts>
constexpr variant_alternative_t<I, rvariant<Ts...>> const&&
get(rvariant<Ts...> const&&);
template<class T, class... Ts> constexpr T& get(rvariant<Ts...>&);
template<class T, class... Ts> constexpr T&& get(rvariant<Ts...>&&);
template<class T, class... Ts> constexpr T const& get(rvariant<Ts...> const&);
template<class T, class... Ts> constexpr T const&& get(rvariant<Ts...> const&&);
template<std::size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>>>
get_if(rvariant<Ts...>*) noexcept;
template<std::size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>> const>
get_if(rvariant<Ts...> const*) noexcept;
template<std::size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>>>
get(rvariant<Ts...>*) noexcept; // compatibility with boost::get
template<std::size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>> const>
get(rvariant<Ts...> const*) noexcept; // compatibility with boost::get
template<class T, class... Ts>
constexpr std::add_pointer_t<T>
get_if(rvariant<Ts...>*) noexcept;
template<class T, class... Ts>
constexpr std::add_pointer_t<T const>
get_if(rvariant<Ts...> const*) noexcept;
template<class T, class... Ts>
constexpr std::add_pointer_t<T>
get(rvariant<Ts...>*) noexcept; // compatibility with boost::get
template<class T, class... Ts>
constexpr std::add_pointer_t<T const>
get(rvariant<Ts...> const*) noexcept; // compatibility with boost::get
// [rvariant.visit], visitation
template<class Visitor, class... Variants>
constexpr see below visit(Visitor&&, Variants&&...);
template<class R, class Visitor, class... Variants>
constexpr R visit(Visitor&&, Variants&&...);
// [rvariant.hash], hash support
template<class... Ts>
/* constexpr */ std::size_t hash_value(rvariant<Ts...> const&);
// [rvariant.hash], hash support
template<class T, class Allocator>
/* constexpr */ std::size_t hash_value(recursive_wrapper<T, Allocator> const&);
// [rvariant.recursive.helper], recursive_wrapper
helper classes
template<class T> struct unwrap_recursive;
template<class T, class Allocator> struct unwrap_recursive<recursive_wrapper<T, Allocator>>;
template<class T> using unwrap_recursive_t = typename unwrap_recursive<T>::type;
// [rvariant.pack], pack manipulation and deduping
template<template<class...> class TT, class A, class B>
struct compact_alternative;
template<template<class...> class TT, class A, class B>
using compact_alternative_t = typename compact_alternative<TT, A, B>::type;
} // temp_ns
namespace std {
// [rvariant.hash], hash support
template<class... Ts> struct hash<::temp_ns::rvariant<Ts...>>;
// [rvariant.hash], hash support
template<class T, class Allocator> struct hash<::temp_ns::recursive_wrapper<T, Allocator>>;
} // std
Class template rvariant
[rvariant.rvariant]
namespace temp_ns {
template<class... Ts>
class rvariant
{
public:
// [rvariant.ctor], constructors
constexpr rvariant::rvariant() noexcept(see below);
constexpr rvariant::rvariant(rvariant const&);
constexpr rvariant::rvariant(rvariant&&) noexcept(see below);
template<class T>
constexpr rvariant(T&&) noexcept(see below);
template<class T, class... Args>
constexpr explicit rvariant(std::in_place_type_t<T>, Args&&...);
template<class T, class U, class... Args>
constexpr explicit rvariant(std::in_place_type_t<T>, std::initializer_list<U>, Args&&...);
template<std::size_t I, class... Args>
constexpr explicit rvariant(std::in_place_index_t<I>, Args&&...);
template<std::size_t I, class U, class... Args>
constexpr explicit rvariant(std::in_place_index_t<I>, std::initializer_list<U>, Args&&...);
// [rvariant.ctor], flexible constructors
template<class... Us>
constexpr rvariant(rvariant<Us...> const&);
template<class... Us>
constexpr rvariant(rvariant<Us...>&&) noexcept(see below);
// [rvariant.dtor], destructor
constexpr ~rvariant();
// [rvariant.assign], assignment
constexpr rvariant& operator=(rvariant const&);
constexpr rvariant& operator=(rvariant&&) noexcept(see below);
template<class T> constexpr rvariant& operator=(T&&) noexcept(see below);
// [rvariant.assign], flexible assignment
template<class... Us>
constexpr rvariant& operator=(rvariant<Us...> const&);
template<class... Us>
constexpr rvariant& operator=(rvariant<Us...>&&) noexcept(see below);
// [rvariant.mod], modifiers
template<class T, class... Args>
constexpr T& emplace(Args&&...);
template<class T, class U, class... Args>
constexpr T& emplace(std::initializer_list<U>, Args&&...);
template<std::size_t I, class... Args>
constexpr variant_alternative_t<I, rvariant<Ts...>>& emplace(Args&&...);
template<std::size_t I, class U, class... Args>
constexpr variant_alternative_t<I, rvariant<Ts...>>&
emplace(std::initializer_list<U>, Args&&...);
// [rvariant.status], value status
constexpr bool valueless_by_exception() const noexcept;
constexpr std::size_t index() const noexcept;
// [rvariant.subset], subset
template<class... Us>
constexpr rvariant<Us...> subset() const& noexcept(see below);
template<class... Us>
constexpr rvariant<Us...> subset() && noexcept(see below);
// [rvariant.swap], swap
constexpr void swap(rvariant&) noexcept(see below);
// [rvariant.visit], visitation
template<class Self, class Visitor>
constexpr decltype(auto) visit(this Self&&, Visitor&&);
template<class R, class Self, class Visitor>
constexpr R visit(this Self&&, Visitor&&);
};
} // temp_ns
General [rvariant.rvariant.general]
See also: spec of std::variant and boost::variant
|
-
Class template
rvariant
follows all requirements ofstd::variant
, unless otherwise noted. -
All types in
Ts
must satisfy all requirements on the corresponding parameter instd::variant
, unless otherwise noted. -
Let
T
andA
denote arbitrary types. For the template parameter ofrvariant
, if a user provides bothT
andrecursive_wrapper<T, A>
, the program is ill-formed. -
Let
T
denote an arbitrary type. For the template parameter ofrvariant
, if a user provides multiple different specializations ofrecursive_wrapper
such that the first template parameter isT
, the program is ill-formed.
Although
|
-
Let
VTi
denoterecursive_wrapper<Ti, A>
(for any typeA
) if such a specialization occurs anywhere inTs...
; otherwise, letVTi
denoteTi
. LetUj
denote the jth type of the template parameter pack having the nameUs
on each flexibility-related functions. The corresponding alternative forrvariant
is the first type for whichstd::is_same_v<unwrap_recursive_t<VTi>, unwrap_recursive_t<Uj>>
istrue
.
Constructors [rvariant.ctor]
constexpr rvariant::rvariant() noexcept(see below);// 1
constexpr rvariant::rvariant(rvariant const& w);// 2
constexpr rvariant::rvariant(rvariant&& w) noexcept(see below);// 3
// Generic constructor
template<class T>
constexpr rvariant(T&& t) noexcept(see below);// 4
template<class T, class... Args>
constexpr explicit rvariant(std::in_place_type_t<T>, Args&&... args);// 5
template<class T, class U, class... Args>
constexpr explicit rvariant(std::in_place_type_t<T>, std::initializer_list<U> il, Args&&...);// 6
template<std::size_t I, class... Args>
constexpr explicit rvariant(std::in_place_index_t<I>, Args&&... args);// 7
template<std::size_t I, class U, class... Args>
constexpr explicit rvariant(std::in_place_index_t<I>, std::initializer_list<U> il, Args&&... args);// 8
// Flexible copy constructor
template<class... Us>
constexpr rvariant(rvariant<Us...> const& w);// 9
// Flexible move constructor
template<class... Us>
constexpr rvariant(rvariant<Us...>&& w) noexcept(see below);// 10
-
1-3) Equivalent to the
std::variant
counterpart. [spec] -
4) Generic constructor. Equivalent to the
std::variant
counterpart, [spec] except:Postconditions:
holds_alternative<unwrap_recursive_t<Tj>>(*this)
istrue
. -
5) Mandates:
T
is not a specialization ofrecursive_wrapper
.Let
VT
denoterecursive_wrapper<T, A>
(for any typeA
) if such a specialization occurs anywhere inTs...
; otherwise, letVT
denoteT
.Constraints:
-
— There is exactly one occurrence of
T
inunwrap_recursive_t<Ts>...
and -
—
std::is_constructible_v<VT, Args...>
istrue
.
Effects: Direct-non-list-initializes the contained value of type
VT
withstd::forward<Args>(args)...
.Postconditions:
holds_alternative<T>(*this)
istrue
.Throws: Any exception thrown by calling the selected constructor of
VT
.Remarks: If
VT
's selected constructor is a constexpr constructor, this constructor is a constexpr constructor. -
-
6) Mandates:
T
is not a specialization ofrecursive_wrapper
.Let
VT
denoterecursive_wrapper<T, A>
(for any typeA
) if such a specialization occurs anywhere inTs...
; otherwise, letVT
denoteT
.Constraints:
-
— There is exactly one occurrence of
T
inunwrap_recursive_t<Ts>...
and -
—
std::is_constructible_v<VT, std::initializer_list<U>&, Args...>
istrue
.
Effects: Direct-non-list-initializes the contained value of type
VT
withil, std::forward<Args>(args)...
.Postconditions:
holds_alternative<T>(*this)
istrue
.Throws: Any exception thrown by calling the selected constructor of
VT
.Remarks: If
VT
's selected constructor is a constexpr constructor, this constructor is a constexpr constructor. -
-
7-8) Equivalent to the
std::variant
counterpart. [spec] -
9) Flexible copy constructor.
Let
VTi
andUj
denote the types defined in [rvariant.rvariant.general].Constraints:
-
—
std::is_same_v<rvariant<Us...>, rvariant>
isfalse
, and -
—
rvariant_set::subset_of<rvariant<Us...>, rvariant>
istrue
, and -
—
std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_t<Ts>>...>
isfalse
, and -
—
std::is_constructible_v<VTi, Uj const&>
istrue
for all j.
Effects: If
w
holds a value, initializes thervariant
to holdVTi
(with i being the index of the alternative corresponding to that ofw
) and direct-initializes the contained value withGET<w.index()>(w)
. Otherwise, initializes thervariant
to not hold a value.Throws: Any exception thrown by direct-initializing any alternative corresponding to that of
w
.Remarks:
-
— The exception specification is equivalent to the logical
AND
ofstd::is_nothrow_constructible_v<VTi, Uj const&>
for all j.
-
-
10) Flexible move constructor.
Let
VTi
andUj
denote the types defined in [rvariant.rvariant.general].Constraints:
-
—
std::is_same_v<rvariant<Us...>, rvariant>
isfalse
, and -
—
rvariant_set::subset_of<rvariant<Us...>, rvariant>
istrue
, and -
—
std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_t<Ts>>...>
isfalse
, and -
—
std::is_constructible_v<VTi, Uj&&>
istrue
for all j.
Effects: If
w
holds a value, initializes thervariant
to holdVTi
(with i being the index of the alternative corresponding to that ofw
) and direct-initializes the contained value withGET<w.index()>(std::move(w))
. Otherwise, initializes thervariant
to not hold a value.Throws: Any exception thrown by move-constructing any alternative corresponding to that of
w
.Remarks:
-
— The exception specification is equivalent to the logical
AND
ofstd::is_nothrow_constructible_v<VTi, Uj&&>
for all j.
-
Destructor [rvariant.dtor]
constexpr ~rvariant();// 1
-
1) Equivalent to the
std::variant
counterpart. [spec]
Assignment [rvariant.assign]
constexpr rvariant& operator=(rvariant const& rhs);// 1
constexpr rvariant& operator=(rvariant&& rhs) noexcept(see below);// 2
// Generic assignment operator
template<class T>
constexpr rvariant& operator=(T&& t) noexcept(see below);// 3
// Flexible copy assignment operator
template<class... Us>
constexpr rvariant& operator=(rvariant<Us...> const& rhs);// 4
// Flexible move assignment operator
template<class... Us>
constexpr rvariant& operator=(rvariant<Us...>&& rhs) noexcept(see below);// 5
-
1-2) Equivalent to the
std::variant
counterpart. [spec] -
3) Generic assignment operator. Equivalent to the
std::variant
counterpart, [spec] except:Postconditions:
holds_alternative<unwrap_recursive_t<Tj>>(*this)
istrue
, withTj
selected by the imaginary function overload resolution described above. -
4) Flexible copy assignment operator.
Let
VTi
andUj
denote the types defined in [rvariant.rvariant.general].Constraints:
-
—
std::is_same_v<rvariant<Us...>, rvariant>
isfalse
, and -
—
rvariant_set::subset_of<rvariant<Us...>, rvariant>
istrue
, and -
—
std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_t<Ts>>...>
isfalse
, and -
—
std::is_constructible_v<VTi, Uj const&> && std::is_assignable_v<VTi&, Uj const&>
istrue
for all j.
Effects: Let j be
rhs.index()
.-
— If neither
*this
norrhs
holds a value, there is no effect. -
— Otherwise, if
*this
holds a value butrhs
does not, destroys the value contained in*this
and sets*this
to not hold a value. -
— Otherwise, if
rhs
holds a value but*this
does not, initializesrvariant
to holdVTi
(with i being the index of the alternative corresponding to that ofrhs
) and direct-initializes the contained value withGET<j>(rhs)
. -
— Otherwise, if
std::is_same_v<unwrap_recursive_t<Ti>, unwrap_recursive_t<Uj>>
istrue
, assignsGET<j>(rhs)
to the value contained in*this
. (Note: the left hand side isTi
, notVTi
. This ensures that the existing storage is reused even forrvariant
with duplicate corresponding alternatives; i.e.index()
is unchanged.) -
— Otherwise, if either
std::is_nothrow_constructible_v<VTi, Uj const&>
istrue
orstd::is_nothrow_move_constructible_v<VTi>
isfalse
, equivalent toemplace<VTi>(GET<j>(rhs))
. -
— Otherwise, equivalent to
emplace<VTi>(VTi(GET<j>(rhs)))
.
Postconditions: If
rhs.valueless_by_exception()
istrue
,index() == rhs.index()
; otherwise,*this
holds the alternative corresponding to that ofrhs
.Returns:
*this
.Remarks: The exception specification is equivalent to the logical
AND
ofstd::is_nothrow_constructible_v<VTi, Uj const&> && std::is_nothrow_assignable_v<VTi&, Uj const&>
for all j. -
-
5) Flexible move assignment operator.
Let
VTi
andUj
denote the types defined in [rvariant.rvariant.general].Constraints:
-
—
std::is_same_v<rvariant<Us...>, rvariant>
isfalse
, -
—
rvariant_set::subset_of<rvariant<Us...>, rvariant>
istrue
, and -
—
std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_t<Ts>>...>
isfalse
, and -
—
std::is_constructible_v<VTi, Uj&&> && std::is_assignable_v<VTi&, Uj&&>
istrue
for all j.
Effects: Let j be
rhs.index()
.-
— If neither
*this
norrhs
holds a value, there is no effect. -
— Otherwise, if
*this
holds a value butrhs
does not, destroys the value contained in*this
and sets*this
to not hold a value. -
— Otherwise, if
rhs
holds a value but*this
does not, initializesrvariant
to holdVTi
(with i being the index of the alternative corresponding to that ofrhs
) and direct-initializes the contained value withGET<j>(std::move(rhs))
. -
— Otherwise, if
std::is_same_v<unwrap_recursive_t<Ti>, unwrap_recursive_t<Uj>>
istrue
, assignsGET<j>(std::move(rhs))
to the value contained in*this
. (Note: the left hand side isTi
, notVTi
. This ensures that the existing storage is reused even forrvariant
with duplicate corresponding alternatives; i.e.index()
is unchanged.) -
— Otherwise, equivalent to
emplace<VTi>(GET<j>(std::move(rhs)))
.
Returns:
*this
.Remarks: The exception specification is equivalent to the logical
AND
ofstd::is_nothrow_constructible_v<VTi, Uj&&> && std::is_nothrow_assignable_v<VTi&, Uj&&>
for all j. -
Modifiers [rvariant.mod]
template<class T, class... Args>
constexpr T& emplace(Args&&... args);// 1
template<class T, class U, class... Args>
constexpr T& emplace(std::initializer_list<U> il, Args&&... args);// 2
template<std::size_t I, class... Args>
constexpr variant_alternative_t<I, rvariant<Ts...>>&
emplace(Args&&... args);// 3
template<std::size_t I, class U, class... Args>
constexpr variant_alternative_t<I, rvariant<Ts...>>&
emplace(std::initializer_list<U> il, Args&&... args);// 4
-
1) Let
VT
denoterecursive_wrapper<T, A>
(for any typeA
) if such a specialization occurs anywhere inTs...
; otherwise, letVT
denoteT
.Mandates:
T
is not a specialization ofrecursive_wrapper
.Constraints:
std::is_constructible_v<VT, Args...>
istrue
, andT
occurs exactly once inunwrap_recursive_t<Ts>
.Effects: Equivalent to:
return emplace<I>(std::forward<Args>(args)...);
whereI
is the zero-based index ofT
inunwrap_recursive_t<Ts>
. -
2) Let
VT
denoterecursive_wrapper<T, A>
(for any typeA
) if such a specialization occurs anywhere inTs...
; otherwise, letVT
denoteT
.Mandates:
T
is not a specialization ofrecursive_wrapper
.Constraints:
std::is_constructible_v<VT, std::initializer_list<U>&, Args...>
istrue
, andT
occurs exactly once inunwrap_recursive_t<Ts>
.Effects: Equivalent to:
return emplace<I>(il, std::forward<Args>(args)...);
whereI
is the zero-based index ofT
inunwrap_recursive_t<Ts>
. -
3) Equivalent to the
std::variant
counterpart, [spec] except:Returns: Let
o
denote a reference to the new contained value. ReturnsUNWRAP_RECURSIVE(o)
.Remarks: If
TI
is a specialization ofrecursive_wrapper
, this function is permitted to construct an intermediate variabletmp
as if by passingstd::forward<Args>(args)...
toTI
's constructor. Thenrvariant
direct-non-list-initializes the contained value ofTI
with the argumentstd::move(tmp)
. (Note: This allows optimization wherervariant
can be assumed to become never valueless on certain cases.) -
4) Equivalent to the
std::variant
counterpart, [spec] except:Returns: Let
o
denote a reference to the new contained value. ReturnsUNWRAP_RECURSIVE(o)
.Remarks: If
TI
is a specialization ofrecursive_wrapper
, this function is permitted to construct an intermediate variabletmp
as if by passingil, std::forward<Args>(args)...
toTI
's constructor. Thenrvariant
direct-non-list-initializes the contained value ofTI
with the argumentstd::move(tmp)
. (Note: This allows optimization wherervariant
can be assumed to become never valueless on certain cases.)
Value status [rvariant.status]
constexpr bool valueless_by_exception() const noexcept;
constexpr std::size_t index() const noexcept;
Equivalent to the std::variant
counterpart. [spec]
Subset [rvariant.subset]
template<class... Us>
requires std::is_same_v<rvariant<Us...>, rvariant>
constexpr rvariant subset() const& noexcept(std::is_nothrow_copy_constructible_v<rvariant>);// 1
template<class... Us>
requires std::is_same_v<rvariant<Us...>, rvariant>
constexpr rvariant subset() && noexcept(std::is_nothrow_move_constructible_v<rvariant>);// 2
template<class... Us>
requires (!std::is_same_v<rvariant<Us...>, rvariant>)
constexpr rvariant<Us...> subset() const& noexcept(see below);// 3
template<class... Us>
requires (!std::is_same_v<rvariant<Us...>, rvariant>)
constexpr rvariant<Us...> subset() && noexcept(see below);// 4
-
1) Returns:
*this
.Throws: Any exception thrown by copy-constructing any type in
Us
. -
2) Returns:
std::move(*this)
.Throws: Any exception thrown by move-constructing any type in
Us
. -
3) Mandates:
std::is_copy_constructible_v<Uj>
istrue
for all j, whereUj
be the jth type inUs
.Effects: If
*this
holds a value, returns anrvariant<Us...>
object that holds the alternative corresponding to that of*this
, with its contained value direct-initialized fromGET<i>(*this)
, where i isthis->index()
. Otherwise, returns anrvariant<Us...>
object that does not hold a value.Throws:
std::bad_variant_access
if*this
holds an alternative that is not contained inUs
; otherwise, equivalent to the semantics of the flexible copy constructor.Remarks:
-
— This function does not participate in overload resolution unless
rvariant_set::subset_of<rvariant<Us...>, rvariant>
istrue
. -
— The exception specification is equivalent to the logical
AND
ofrvariant_set::equivalent_to<rvariant<Us...>, rvariant>
andstd::is_nothrow_constructible_v<rvariant<Us...>, rvariant const&>
. -
— The corresponding index on the returned
rvariant<Us...>
object shall be determined according to the rules defined in the flexible copy constructor.
-
-
4) Equivalent to the overload #3, except:
-
— Citation of flexible copy constructor is replaced with flexible move constructor.
Mandates:
std::is_move_constructible_v<Uj>
istrue
for all j, whereUj
be the jth type inUs
.Effects:
GET<i>(*this)
is replaced withGET<i>(std::move(*this))
.Remarks:
std::is_nothrow_constructible_v<rvariant<Us...>, rvariant const&>
is replaced withstd::is_nothrow_constructible_v<rvariant<Us...>, rvariant&&>
. -
Swap [rvariant.swap]
constexpr void swap(rvariant&) noexcept(see below);
Equivalent to the std::variant
counterpart. [spec]
rvariant
helper classes [rvariant.helper]
namespace temp_ns {
template<std::size_t I, class T>
struct variant_alternative; // not defined
template<std::size_t I, class T>
struct variant_alternative<I, T const>;// 1
template<std::size_t I, class... Ts>
struct variant_alternative<I, rvariant<Ts...>>;// 2
} // temp_ns
-
1) Equivalent to the
std::variant
counterpart. [spec] -
2) The member typedef
type
denotesunwrap_recursive_t<TI>
.Mandates:
I < sizeof...(Ts)
.
Flexibility traits [rvariant.flex]
namespace temp_ns::rvariant_set {
template<class W, class V>
struct is_subset_of : std::false_type {};// 1
template<class... Us, class... Ts>
struct is_subset_of<rvariant<Us...>, rvariant<Ts...>>;// 2
template<class W, class V>
constexpr bool is_subset_of_v = is_subset_of<W, V>::value;
template<class W, class V>
concept subset_of = is_subset_of_v<W, V>;
template<class W, class V>
concept equivalent_to = subset_of<W, V> && subset_of<V, W>;
} // temp_ns::rvariant_set
-
1) Mandates: Both
W
andV
are specialization ofrvariant
. -
2) Constraints: For every type
U
inUs
, there exists at least one typeT
inTs
such that:-
—
T
is the same type asU
or, -
—
unwrap_recursive_t<T>
is the same type asU
.
-
Value access [rvariant.get]
namespace temp_ns {
template<class T, class... Ts>
constexpr bool holds_alternative(rvariant<Ts...> const& v) noexcept;
} // temp_ns
-
Mandates: The type
T
occurs exactly once inunwrap_recursive_t<Ts>
.Returns:
true
ifv.index()
is equal to the zero-based index ofT
inunwrap_recursive_t<Ts>
.Remarks: This function is defined as deleted if
T
is a specialization ofrecursive_wrapper
.
template<std::size_t I, class... Ts>
constexpr see below& GET(rvariant<Ts...>& v); // exposition only
template<std::size_t I, class... Ts>
constexpr see below&& GET(rvariant<Ts...>&& v); // exposition only
template<std::size_t I, class... Ts>
constexpr see below const& GET(rvariant<Ts...> const& v); // exposition only
template<std::size_t I, class... Ts>
constexpr see below const&& GET(rvariant<Ts...> const&& v); // exposition only
-
Mandates:
I < sizeof...(Ts)
.Preconditions:
v.index()
isI
.Returns:
o
, whereo
denotes a reference to the object stored inv
, if the type of the expression’s receiver is a specialization ofrecursive_wrapper
; otherwise, returnsUNWRAP_RECURSIVE(o)
.
namespace temp_ns {
template<std::size_t I, class... Ts>
constexpr variant_alternative_t<I, rvariant<Ts...>>&
get(rvariant<Ts...>& v);
template<std::size_t I, class... Ts>
constexpr variant_alternative_t<I, rvariant<Ts...>>&&
get(rvariant<Ts...>&& v);
template<std::size_t I, class... Ts>
constexpr variant_alternative_t<I, rvariant<Ts...>> const&
get(rvariant<Ts...> const& v);
template<std::size_t I, class... Ts>
constexpr variant_alternative_t<I, rvariant<Ts...>> const&&
get(rvariant<Ts...> const&& v);
} // temp_ns
-
Mandates:
I < sizeof...(Ts)
.Effects: If
v.index()
isI
, returnsUNWRAP_RECURSIVE(o)
, whereo
denotes a reference to the object stored in thervariant
. Otherwise, throws an exception of typestd::bad_variant_access
.
namespace temp_ns {
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
-
Mandates: The type
T
occurs exactly once inunwrap_recursive_t<Ts>
.Effects: Let
VT
denote the type of the alternative held byv
. Ifunwrap_recursive_t<VT>
is the same type asT
, returnsUNWRAP_RECURSIVE(o)
, whereo
denotes a reference to the object stored in thervariant
. Otherwise, throws an exception of typestd::bad_variant_access
.Remarks: This function is defined as deleted if
T
is a specialization ofrecursive_wrapper
.
namespace temp_ns {
template<std::size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>>>
get_if(rvariant<Ts...>*) noexcept;// 1
template<std::size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>> const>
get_if(rvariant<Ts...> const* v) noexcept;// 2
template<std::size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>>>
get(rvariant<Ts...>*) noexcept; // compatibility with boost::get
// 3
template<std::size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>> const>
get(rvariant<Ts...> const* v) noexcept; // compatibility with boost::get
// 4
} // temp_ns
-
1-2) Mandates:
I < sizeof...(Ts)
.Returns: A pointer to the value denoted by
UNWRAP_RECURSIVE(o)
, whereo
denotes a reference to the object stored in thervariant
, ifv != nullptr
andv->index() == I
. Otherwise, returnsnullptr
. -
3-4) Provided for compatibility with
boost::get
.Returns:
get_if<I>(v)
.
namespace temp_ns {
template<class T, class... Ts>
constexpr std::add_pointer_t<T>
get_if(rvariant<Ts...>* v) noexcept;// 1
template<class T, class... Ts>
constexpr std::add_pointer_t<T const>
get_if(rvariant<Ts...> const* v) noexcept;// 2
template<class T, class... Ts>
constexpr std::add_pointer_t<T>
get(rvariant<Ts...>* v) noexcept; // compatibility with boost::get
// 3
template<class T, class... Ts>
constexpr std::add_pointer_t<T const>
get(rvariant<Ts...> const* v) noexcept; // compatibility with boost::get
// 4
} // temp_ns
-
1-2) Mandates: The type
T
occurs exactly once inunwrap_recursive_t<Ts>
.Effects: Equivalent to:
return get_if<i>(v);
with i being the zero-based index ofT
inunwrap_recursive_t<Ts>
.Remarks: This function is defined as deleted if
T
is a specialization ofrecursive_wrapper
. -
3-4) Provided for compatibility with
boost::get
.Returns:
get_if<T>(v)
.
Visitation [rvariant.visit]
namespace temp_ns {
template<class Visitor, class... Variants>
constexpr see below visit(Visitor&& vis, Variants&&... vars);// 1
template<class R, class Visitor, class... Variants>
constexpr R visit(Visitor&& vis, Variants&&... vars);// 2
} // temp_ns
// below are member functions of the class template rvariant
:
template<class Self, class Visitor>
constexpr decltype(auto) visit(this Self&& self, Visitor&& vis);// 3
template<class R, class Self, class Visitor>
constexpr R visit(this Self&& self, Visitor&& vis);// 4
-
1-2) Equivalent to the
std::variant
counterpart [spec], except that:-
—
GET<m>(std::forward<V>(vars))
is replaced withUNWRAP_RECURSIVE(GET<m>(std::forward<V>(vars)))
.
-
-
3-4) Equivalent to the
std::variant
counterpart [spec], except that it forwards totemp_ns::visit
instead ofstd::visit
.
Hash support [rvariant.hash]
namespace std {
template<class... Ts>
struct hash<::temp_ns::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, class Allocator>
/* constexpr */ std::size_t hash_value(recursive_wrapper<T, Allocator> const& rw);// 4
} // temp_ns
I/O [rvariant.io]
I/O components are not included by the global convenience header (<temp_ns/rvariant.hpp>
).
operator<<
support
// <temp_ns/rvariant/rvariant_io.hpp>
#include <ostream>
namespace temp_ns {
template<class T>
constexpr bool ADL-ostreamable = see below; // exposition only// 1
template<class... Ts>
std::ostream& operator<<(std::ostream& os, rvariant<Ts...> const& v);// 2
} // temp_ns
-
1) Evaluates to
true
if all of the following conditions are met; otherwise, evaluates tofalse
.-
— Let
os
denote an lvalue reference to an object of typestd::ostream
, and letval
denote an lvalue reference to an object of typeT
. These references are valid in unevaluated context, and -
— the expression
os << val
is well-formed and has the typestd::ostream&
, and -
— the corresponding overload is found solely via ADL.
-
-
2) Constraints:
ADL-ostreamable<unwrap_recursive_t<Ti>>
istrue
for all i.Effects: Behaves as a formatted output function ([ostream.formatted.reqmts]) of
os
, except that:-
— the output is done as if by calling
os << UNWRAP_RECURSIVE(GET<i>(v))
(with i beingv.index()
), and -
— any exception of type
std::bad_variant_access
, whether thrown directly (i.e. due tov
being valueless) or indirectly (i.e. by a nested call to an alternative’s output function), is propagated without regard to the value ofos.exceptions()
and without turning onstd::ios_base::badbit
in the error state ofos
.
Returns:
os
.Throws:
std::bad_variant_access
ifv.valueless_by_exception()
istrue
. Otherwise, throws any exception thrown as per the formatted output function’s specification. -
std::formatter
support
-
Let
v
denote an object ofrvariant
, and letproxy
denote an object ofvariant_format_proxy
. -
The specialization
std::formatter<::temp_ns::rvariant<Ts...>, charT>
(for arbitrarycharT
) is enabled if and only ifstd::formattable<unwrap_recursive_t<Tsi>, charT>
istrue
for all i, with the following characteristics:-
— The format specifier must be empty, otherwise
std::format_error
is thrown, and -
— if
v.valueless_by_exception()
istrue
,std::bad_variant_access
is thrown, and -
— the output is done as if by calling
std::format_to(fmt_ctx.out(), paren, UNWRAP_RECURSIVE(GET<v.index()>(v)))
, withparen
being a string literal"{}"
interpreted on the target character type. -
Example:
std::println("{}", temp_ns::rvariant<int, double>(42)); // prints
42
-
-
The specialization
std::formatter<variant_format_proxy<VFormat, Variant>, charT>
is enabled if and only if:-
—
std::remove_cvref_t<VFormat>
is a specialization ofvariant_format_string
, and -
—
std::remove_cvref_t<Variant>
is a specialization ofrvariant
, and -
—
std::formattable<unwrap_recursive_t<Tsi>, charT>
istrue
for all i, withTs
being the template parameter pack of cv-unqualified non-reference type forVariant
.
-
-
It has the following characteristics:
-
— The format specifier must be empty, otherwise
std::format_error
is thrown, and -
— if
v.valueless_by_exception()
istrue
,std::bad_variant_access
is thrown, and -
— the output is done as if by calling
std::format_to(fmt_ctx.out(), proxy.v_fmt(std::in_place_type<Ts...[proxy.v.index()]>), UNWRAP_RECURSIVE(GET<proxy.v.index()>(proxy.v)))
, withTs
being the template parameter pack of the cv-unqualified non-reference type ofproxy.v
. -
Example:
using V = temp_ns::rvariant<int, double>; constexpr auto v_fmt = temp_ns::variant_format_for<V>("{:04d}", "{:.1f}"); std::println("foo{}bar", temp_ns::format_by(v_fmt, V(42)); // prints
foo0042bar
std::println("foo{}bar", temp_ns::format_by(v_fmt, V(3.14)); // printsfoo3.1bar
-
template<class... CharLike>
using select-char-t = see below; // exposition only
-
Denotes
charT
, withcharT
being the character type for whichstd::is_convertible_v<CharLikei, std::basic_string_view<charT>>
istrue
for all i. If there exists no such substitution, the program is ill-formed.
// <temp_ns/rvariant/rvariant_io.hpp>
#include <format>
template<class charT, class... Ts>
struct variant_format_string // exposition only
{
std::basic_format_string<charT, Ts...[i] const&> fmts...;
auto const& operator()(std::in_place_type<Ts...[i]>) const noexcept { return fmts...[i]; }
};
namespace temp_ns {
template<class... Ts, class... Fmts>
constexpr variant_format_string<see below> variant_format(Fmts&&... fmts) noexcept;// 1
template<class Variant, class... Fmts>
constexpr variant_format_string<see below> variant_format_for(Fmts&&... fmts) noexcept;// 2
} // temp_ns
-
1) Mandates:
std::is_convertible_v<Fmtsi, std::basic_format_string<select-char-t<Fmts...>, Tsi const&>>
istrue
for all i, andsizeof...(Ts) > 0
istrue
.Let
charT
denoteselect-char-t<Fmts...>
.Returns:
variant_format_string<charT, Ts...>{std::forward<Fmts>(fmts)...}
. -
2) Mandates:
std::is_convertible_v<Fmtsi, std::basic_format_string<select-char-t<Fmts...>, Tsi const&>>
istrue
for all i, withTs
being the template parameter pack of cv-unqualified non-reference type forVariant
. Such substitution is valid only ifstd::remove_cvref_t<Variant>
is a specialization ofrvariant
.Let
charT
denoteselect-char-t<Fmts...>
.Returns:
variant_format_string<charT, Ts...>{std::forward<Fmts>(fmts)...}
.
// <temp_ns/rvariant/rvariant_io.hpp>
#include <format>
template<class VFormat, class Variant>
struct variant_format_proxy // exposition only
{
VFormat v_fmt;
Variant v;
};
namespace temp_ns {
template<class VFormat, class Variant>
constexpr variant_format_proxy<VFormat, Variant>
format_by(VFormat&& v_fmt, Variant&& v) noexcept;// 1
} // temp_ns
-
1) Constraints:
std::remove_cvref_t<VFormat>
is a specialization ofvariant_format_string
, andstd::remove_cvref_t<Variant>
is a specialization ofrvariant
.Returns:
variant_format_proxy<VFormat, Variant>{std::forward<VFormat>(v_fmt), std::forward<Variant>(v)}
.
Class template recursive_wrapper
[rvariant.recursive]
#include <compare>
#include <memory>
namespace temp_ns {
template<class T, class Allocator = std::allocator<T>>
class recursive_wrapper
{
// provides the same functionality as std::indirect
, unless otherwise noted
// [rvariant.recursive.ctor], constructors
constexpr /* not explicit */ recursive_wrapper();
template<class U = T>
constexpr explicit(see below) recursive_wrapper(U&& x) noexcept(see below);
};
// equivalent to the std::indirect
counterpart
template<class Value>
recursive_wrapper(Value) -> recursive_wrapper<Value>;
// equivalent to the std::indirect
counterpart
template<class Allocator, class Value>
recursive_wrapper(std::allocator_arg_t, Allocator, Value)
-> recursive_wrapper<
Value,
typename std::allocator_traits<Allocator>::template rebind_alloc<Value>
>;
} // temp_ns
// <temp_ns/rvariant/recursive_wrapper_pmr.hpp>
#include <memory_resource>
namespace temp_ns::pmr {
template<class T>
using recursive_wrapper = ::temp_ns::recursive_wrapper<T, std::pmr::polymorphic_allocator<T>>;
} // temp_ns::pmr
General [rvariant.recursive.general]
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:
-
— The class name is
recursive_wrapper
. -
—
std::indirect
andtemp_ns::recursive_wrapper
are distinguishable in type level.
temp_ns::recursive_wrapper is not a type alias of std::indirect and does not publicly derive from it.
|
Although std::indirect is a C++26 feature, temp_ns::recursive_wrapper can be used in C++23.
|
Constructors
Effectively overrides only the ones listed below; rest are the same as std::indirect
counterparts. [spec]
constexpr /* not explicit */ recursive_wrapper();// 1
template<class U = T>
constexpr explicit(see below) recursive_wrapper(U&& u) noexcept(see below);// 2
-
1) Equivalent to the
std::indirect
counterpart,[spec] except that it is notexplicit
. -
2) Constraints:
-
—
std::is_same_v<std::remove_cvref_t<U>, recursive_wrapper>
isfalse
, and -
—
std::is_same_v<std::remove_cvref_t<U>, std::in_place_t>
isfalse
, and -
—
std::is_default_constructible_v<Allocator>
istrue
, and -
—
std::is_convertible_v<U, T>
istrue
, or the expressionT x[] = {std::forward<U>(u)}
(for an imaginary variablex
) is well-formed in unevaluated context. (Note: This prevents recursive instantiation ofstd::is_constructible
, even for recursive types, while preserving SFINAE-friendliness.)
Effects: Equivalent to the
std::indirect
counterpart. [spec]Remarks: The explicit specifier is equivalent to
!std::is_convertible_v<U, T>
. -
recursive_wrapper
helper classes [rvariant.recursive.helper]
namespace temp_ns {
template<class T>
struct unwrap_recursive
{
using type = T;
};
template<class T, class Allocator>
struct unwrap_recursive<recursive_wrapper<T, Allocator>>
{
using type = T;
};
} // temp_ns
template<class T>
constexpr see below UNWRAP_RECURSIVE(T&& o) noexcept; // exposition only
-
Effects: Denotes
*o
, if cv-unqualified non-reference type forT
is a specialization ofrecursive_wrapper
. Otherwise, denoteso
.
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
andB
, 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 whereTT<Ts...>
is the same type aspack-union-t<TT, A, B>
. The member typedeftype
denotesTs...[0]
ifsizeof...(Ts) == 1
; otherwise, the member typedeftype
denotesTT<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_t 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 ofA
ifA
is a specialization ofTT
, otherwise letAs
denote a pack of single typeA
. LetBs
denote likewise.pack-union-t
denotesTT<Ts...>
, whereTs...
is the set union ofAs...
andBs...
expanded from left to right. For duplicate types, the first occurrence shall remain inTs...
.
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 ifT
is a specialization ofTT
; otherwise, inheritsstd::false_type
. -
3) If
any-ttp
is a template template parameter that consists of NTTP, equivalent tois-nttp-specialization-of
; otherwise, equivalent tois-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
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
This library is distributed under the Boost Software License, Version 1.0.