Overview
-Description
-This library implements a type-safe discriminated/tagged union type,
-variant<T…>
, that is API-compatible with the C++17 Standard’s
-std::variant<T…>
.
A variant<T1, T2, …, Tn>
variable can hold a value of any of the
-types T1
, T2
, …, Tn
. For example,
-variant<int64_t, double, std::string>
can hold an int64_t
value, a
-double
value, or a string
value.
Such a type is sometimes called a "tagged union", because it’s roughly -equivalent to
-struct V
-{
- enum tag { tag_int64_t, tag_double, tag_string };
-
- tag tag_;
-
- union
- {
- int64_t i_;
- double d_;
- std::string s_;
- };
-};
-Usage Examples
-Variants can be used to represent dynamically-typed values. A configuration -file of the form
-server.host=test.example.com
-server.port=9174
-cache.max_load=0.7
-can be represented as std::map<std::string, variant<int64_t, double,
-std::string>>
.
Variants can also represent polymorphism. To take a classic example, a -polymorphic collection of shapes:
-#define _USE_MATH_DEFINES
-#include <iostream>
-#include <vector>
-#include <memory>
-#include <cmath>
-
-class Shape
-{
-public:
-
- virtual ~Shape() = default;
- virtual double area() const = 0;
-};
-
-class Rectangle: public Shape
-{
-private:
-
- double width_, height_;
-
-public:
-
- Rectangle( double width, double height ):
- width_( width ), height_( height ) {}
-
- virtual double area() const { return width_ * height_; }
-};
-
-class Circle: public Shape
-{
-private:
-
- double radius_;
-
-public:
-
- explicit Circle( double radius ): radius_( radius ) {}
- virtual double area() const { return M_PI * radius_ * radius_; }
-};
-
-double total_area( std::vector<std::unique_ptr<Shape>> const & v )
-{
- double s = 0.0;
-
- for( auto const& p: v )
- {
- s += p->area();
- }
-
- return s;
-}
-
-int main()
-{
- std::vector<std::unique_ptr<Shape>> v;
-
- v.push_back( std::unique_ptr<Shape>( new Circle( 1.0 ) ) );
- v.push_back( std::unique_ptr<Shape>( new Rectangle( 2.0, 3.0 ) ) );
-
- std::cout << "Total area: " << total_area( v ) << std::endl;
-}
-can instead be represented as a collection of variant<Rectangle, Circle>
-values. This requires the possible Shape
types be known in advance, as is
-often the case. In return, we no longer need virtual functions, or to allocate
-the values on the heap with new Rectangle
and new Circle
:
#define _USE_MATH_DEFINES
-#include <iostream>
-#include <vector>
-#include <cmath>
-
-#include <boost/variant2/variant.hpp>
-using namespace boost::variant2;
-
-struct Rectangle
-{
- double width_, height_;
- double area() const { return width_ * height_; }
-};
-
-struct Circle
-{
- double radius_;
- double area() const { return M_PI * radius_ * radius_; }
-};
-
-double total_area( std::vector<variant<Rectangle, Circle>> const & v )
-{
- double s = 0.0;
-
- for( auto const& x: v )
- {
- s += visit( []( auto const& y ){ return y.area(); }, x );
- }
-
- return s;
-}
-
-int main()
-{
- std::vector<variant<Rectangle, Circle>> v;
-
- v.push_back( Circle{ 1.0 } );
- v.push_back( Rectangle{ 2.0, 3.0 } );
-
- std::cout << "Total area: " << total_area( v ) << std::endl;
-}
-Construction and Assignment
-If we look at the
- v.push_back( Circle{ 1.0 } );
-line, we can deduce that variant<Rectangle, Circle>
can be (implicitly)
-constructed from Circle
(and Rectangle
), and indeed it can. It can also
-be assigned a Circle
or a Rectangle
:
variant<Rectangle, Circle> v = Circle{ 1.0 }; // v holds Circle
-v = Rectangle{ 2.0, 3.0 }; // v now holds Rectangle
-If we try to construct variant<int, float>
from something that is neither
-int
nor float
, say, (short)1
, the behavior is "as if" the variant
has
-declared two constructors,
variant::variant(int x);
-variant::variant(float x);
-and the standard overload resolution rules are used to pick the one that will
-be used. So variant<int, float>((short)1)
will hold an int
.
Inspecting the Value
-Putting values into a variant
is easy, but taking them out is necessarily a
-bit more convoluted. It’s not possible for variant<int, float>
to define a
-member function get() const
, because such a function will need its return
-type fixed at compile time, and whether the correct return type is int
or
-float
will only become known at run time.
There are a few ways around that. First, there is the accessor member function
-std::size_t variant::index() const noexcept;
-that returns the zero-based index of the current type. For variant<int,
-float>
, it will return 0
for int
and 1
for float
.
Once we have the index, we can use the free function get<N>
to obtain the
-value. Since we’re passing the type index to get
, it knows what to return.
-get<0>(v)
will return int
, and get<1>(v)
will return float
:
void f( variant<int, float> const& v )
-{
- switch( v.index() )
- {
- case 0:
-
- // use get<0>(v)
- break;
-
- case 1:
-
- // use get<1>(v)
- break;
-
- default:
-
- assert(false); // never happens
- }
-}
-If we call get<0>(v)
, and v.index()
is not currently 0
, an exception
-(of type bad_variant_access
) will be thrown.
An alternative approach is to use get<int>(v)
or get<float>(v)
. This
-works similarly.
Another alternative that avoids the possibility of bad_variant_access
is
-to use get_if
. Instead of a reference to the contained value, it returns
-a pointer to it, returning nullptr
to indicate type mismatch. get_if
-takes a pointer to the variant
, so in our example we’ll use something along
-the following lines:
void f( variant<int, float> const& v )
-{
- if( int const * p = get_if<int>(&v) )
- {
- // use *p
- }
- else if( float const * p = get_if<float>(&v) )
- {
- // use *p
- }
- else
- {
- assert(false); // never happens
- }
-}
-Visitation
-Last but not least, there’s visit
. visit(f, v)
calls the a function object
-f
with the value contained in the variant
v
and returns the result. When
-v
is variant<int, float>
, it will call f
with either an int
or a
-float
. The function object must be prepared to accept both.
In practice, this can be achieved by having the function take a type that can
-be passed either int
or float
, such as double
:
double f( double x ) { return x; }
-
-double g( variant<int, float> const& v )
-{
- return visit( f, v );
-}
-By using a function object with an overloaded operator()
:
struct F
-{
- void operator()(int x) const { /* use x */ }
- void operator()(float x) const { /* use x */ }
-};
-
-void g( variant<int, float> const& v )
-{
- visit( F(), v );
-}
-Or by using a polymorphic lambda, as we did in our Circle
/Rectangle
-example:
void g( variant<int, float> const& v )
-{
- visit( [&]( auto const& x ){ std::cout << x << std::endl; }, v );
-}
-visit
can also take more than one variant
. visit(f, v1, v2)
calls
-f(x1, x2)
, where x1
is the value contained in v1
and x2
is the value
-in v2
.
Default Construction
-The default constructor of variant
value-initializes the first type in
-the list. variant<int, float>{}
holds 0
(of type int
), and
-variant<float, int>{}
holds 0.0f
.
This is usually the desired behavior. However, in cases such as
-variant<std::mutex, std::recursive_mutex>
, one might legitimately wish to
-avoid constructing a std::mutex
by default. A provided type, monostate
,
-can be used as the first type in those scenarios. variant<monostate,
-std::mutex, std::recursive_mutex>
will default-construct a monostate
,
-which is basically a no-op, as monostate
is effectively an empty struct
.
Revision History
-Changes in 1.71.0
-After the Boost formal review, the implementation has been
-changed to provide the strong exception safety guarantee,
-instead of basic. expected
has been removed.
Design
-Features
-This variant
implementation has two distinguishing features:
-
-
-
-
It’s never "valueless", that is,
-variant<T1, T2, …, Tn>
has an -invariant that it always contains a valid value of one of the types -T1
,T2
, …,Tn
.
- -
-
It provides the strong exception safety guarantee on assignment and -
-emplace
.
-
This is achieved with the use of double storage, unless all of the -contained types have a non-throwing move constructor.
-Rationale
-Never Valueless
-It makes intuitive sense that variant<X, Y, Z>
can hold only values
-of type X
, type Y
, or type Z
, and nothing else.
If we think of variant
as an extension of union
, since a union
-has a state called "no active member", an argument can be made that a
-variant<X, Y, Z>
should also have such an additional state, holding
-none of X
, Y
, Z
.
This however makes variant
less convenient in practice and less useful
-as a building block. If we really need a variable that only holds X
,
-Y
, or Z
, the additional empty state creates complications that need
-to be worked around. And in the case where we do need this additional
-empty state, we can just use variant<empty, X, Y, Z>
, with a suitable
-struct empty {};
.
From a pure design perspective, the case for no additional empty state is -solid. Implementation considerations, however, argue otherwise.
-When we replace the current value of the variant
(of, say, type X
) with
-another (of type Y
), since the new value needs to occupy the same storage
-as the old one, we need to destroy the old X
first, then construct a new
-Y
in its place. But since this is C++, the construction can fail with an
-exception. At this point the variant
is in the "has no active member"
-state that we’ve agreed it cannot be in.
This is a legitimate problem, and it is this problem that makes having
-an empty/valueless state so appealing. We just leave the variant
empty on
-exception and we’re done.
As explained, though, this is undesirable from a design perspective as it -makes the component less useful and less elegant.
-There are several ways around the issue. The most straightforward one is to
-just disallow types whose construction can throw. Since we can always create
-a temporary value first, then use the move constructor to initialize the one
-in the variant
, it’s enough to require a nonthrowing move constructor,
-rather than all constructors to be nonthrowing.
Unfortunately, under at least one popular standard library implementation,
-node based containers such as std::list
and std::map
have a potentially
-throwing move constructor. Disallowing variant<X, std::map<Y, Z>>
is hardly
-practical, so the exceptional case cannot be avoided.
On exception, we could also construct some other value, leaving the variant
-valid; but in the general case, that construction can also throw. If one of
-the types has a nonthrowing default constructor, we can use it; but if not,
-we can’t.
The approach Boost.Variant takes here is to allocate a temporary copy of
-the value on the heap. On exception, a pointer to that temporary copy can be
-stored into the variant
. Pointer operations don’t throw.
Another option is to use double buffering. If our variant
occupies twice
-the storage, we can construct the new value in the unused half, then, once
-the construction succeeds, destroy the old value in the other half.
When std::variant
was standardized, none of those approaches was deemed
-palatable, as all of them either introduce overhead or are too restrictive
-with respect to the types a variant
can contain. So as a compromise,
-std::variant
took a way that can (noncharitably) be described as "having
-your cake and eating it too."
Since the described exceptional situation is relatively rare, std::variant
-has a special case, called "valueless", into which it goes on exception,
-but the interface acknowledges its existence as little as possible, allowing
-users to pretend that it doesn’t exist.
This is, arguably, not that bad from a practical point of view, but it leaves -many of us wanting. Rare states that "never" occur are undertested and when -that "never" actually happens, it’s usually in the most inconvenient of times.
-This implementation does not follow std::variant
; it statically guarantees
-that variant
is never in a valueless state. The function
-valueless_by_exception
is provided for compatibility, but it always returns
-false
.
Instead, if the contained types are such that it’s not possible to avoid an -exceptional situation when changing the contained value, double storage is -used.
-Strong Exception Safety
-The initial submission only provided the basic exception safety guarantee.
-If an attempt to change the contained value (via assignment or emplace
)
-failed with an exception, and a type with a nonthrowing default constructor
-existed among the alternatives, a value of that type was created into the
-variant
. The upside of this decision was that double storage was needed
-less frequently.
The reviewers were fairly united in hating it. Constructing a random type
-was deemed too unpredictable and not complying with the spirit of the
-basic guarantee. The default constructor of the chosen type, even if
-nonthrowing, may still have undesirable side effects. Or, if not that, a
-value of that type may have special significance for the surrounding code.
-Therefore, some argued, the variant
should either remain with its
-old value, or transition into the new one, without synthesizing other
-states.
At the other side of the spectrum, there were those who considered double -storage unacceptable. But they considered it unacceptable in principle, -regardless of the frequency with which it was used.
-As a result, providing the strong exception safety guarantee on assignment
-and emplace
was declared an acceptance condition.
In retrospect, this was the right decision. The reason the strong guarantee
-is generally not provided is because it doesn’t compose. When X
and Y
-provide the basic guarantee on assignment, so does struct { X x; Y y; };
.
-Similarly, when X
and Y
have nonthrowing assignments, so does the
-struct
. But this doesn’t hold for the strong guarantee.
The usual practice is to provide the basic guarantee on assignment and
-let the user synthesize a "strong" assignment out of either a nonthrowing
-swap
or a nonthrowing move assignment. That is, given x1
and x2
of
-type X
, instead of the "basic" x1 = x2;
, use either X(x2).swap(x1);
-or x1 = X(x2);
.
Nearly all types provide a nonthrowing swap
or a nonthrowing move
-assignment, so this works well. Nearly all, except variant
, which in the
-general case has neither a nonthrowing swap
nor a nonthrowing move
-assignment. If variant
does not provide the strong guarantee itself, it’s
-impossible for the user to synthesize it.
So it should, and so it does.
-Differences with std::variant
-The main differences between this implementation and std::variant
are:
-
-
-
-
No valueless-by-exception state:
-valueless_by_exception()
always -returnsfalse
.
- -
-
Strong exception safety guarantee on assignment and
-emplace
.
- -
-
-emplace
first constructs the new value and then destroys the old one; -in the single storage case, this translates to constructing a temporary -and then moving it into place.
- -
-
A converting constructor from, e.g.
-variant<int, float>
to -variant<float, double, int>
is provided as an extension.
- -
-
The reverse operation, going from
-variant<float, double, int>
to -variant<int, float>
is provided as the member functionsubset<U…>
. -(This operation can throw if the current state of the variant cannot be -represented.)
- -
-
-variant<T…>
is not (yet) trivial when all contained types are trivial, -as mandated by C++17.
- -
-
The C++20 additions and changes to
-std::variant
have not yet been -implemented.
-
Differences with Boost.Variant
-This library is API compatible with std::variant
. As such, its interface
-is different from Boost.Variant’s. For example, visitation is performed via
-visit
instead of apply_visitor
.
Recursive variants are not supported.
-Double storage is used instead of temporary heap backup. This variant
is
-always "stack-based", it never allocates, and never throws bad_alloc
on
-its own.
Implementation
- -Reference
-<boost/variant2/variant.hpp>
-Synopsis
-namespace boost {
-namespace variant2 {
-
-// in_place_type
-
-template<class T> struct in_place_type_t {};
-template<class T> constexpr in_place_type_t<T> in_place_type{};
-
-// in_place_index
-
-template<std::size_t I> struct in_place_index_t {};
-template<std::size_t I> constexpr in_place_index_t<I> in_place_index{};
-
-// variant
-
-template<class... T> class variant;
-
-// variant_size
-
-template<class T> struct variant_size {};
-
-template<class T> struct variant_size<T const>: variant_size<T> {};
-template<class T> struct variant_size<T volatile>: variant_size<T> {};
-template<class T> struct variant_size<T const volatile>: variant_size<T> {};
-
-template<class T> struct variant_size<T&>: variant_size<T> {}; // extension
-template<class T> struct variant_size<T&&>: variant_size<T> {}; // extension
-
-template<class T>
- inline constexpr size_t variant_size_v = variant_size<T>::value;
-
-template<class... T>
- struct variant_size<variant<T...>>:
- std::integral_constant<std::size_t, sizeof...(T)> {};
-
-// variant_alternative
-
-template<size_t I, class T> struct variant_alternative {};
-
-template<size_t I, class T> struct variant_alternative<I, T const>;
-template<size_t I, class T> struct variant_alternative<I, T volatile>;
-template<size_t I, class T> struct variant_alternative<I, T const volatile>;
-
-template<size_t I, class T> struct variant_alternative<I, T&>; // extension
-template<size_t I, class T> struct variant_alternative<I, T&&>; // extension
-
-template<size_t I, class T>
- using variant_alternative_t = typename variant_alternative<I, T>::type;
-
-template<size_t I, class... T>
- struct variant_alternative<I, variant<T...>>;
-
-// variant_npos
-
-constexpr std::size_t variant_npos = -1;
-
-// holds_alternative
-
-template<class U, class... T>
- constexpr bool holds_alternative(const variant<T...>& v) noexcept;
-
-// get
-
-template<size_t I, class... T>
- constexpr variant_alternative_t<I, variant<T...>>&
- get(variant<T...>& v);
-template<size_t I, class... T>
- constexpr variant_alternative_t<I, variant<T...>>&&
- get(variant<T...>&& v);
-template<size_t I, class... T>
- constexpr const variant_alternative_t<I, variant<T...>>&
- get(const variant<T...>& v);
-template<size_t I, class... T>
- constexpr const variant_alternative_t<I, variant<T...>>&&
- get(const variant<T...>&& v);
-
-template<class U, class... T>
- constexpr U& get(variant<T...>& v);
-template<class U, class... T>
- constexpr U&& get(variant<T...>&& v);
-template<class U, class... T>
- constexpr const U& get(const variant<T...>& v);
-template<class U, class... T>
- constexpr const U&& get(const variant<T...>&& v);
-
-// get_if
-
-template<size_t I, class... T>
- constexpr add_pointer_t<variant_alternative_t<I, variant<T...>>>
- get_if(variant<T...>* v) noexcept;
-template<size_t I, class... T>
- constexpr add_pointer_t<const variant_alternative_t<I, variant<T...>>>
- get_if(const variant<T...>* v) noexcept;
-
-template<class U, class... T>
- constexpr add_pointer_t<U>
- get_if(variant<T...>* v) noexcept;
-template<class U, class... T>
- constexpr add_pointer_t<const U>
- get_if(const variant<T...>* v) noexcept;
-
-// relational operators
-
-template<class... T>
- constexpr bool operator==(const variant<T...>& v, const variant<T...>& w);
-template<class... T>
- constexpr bool operator!=(const variant<T...>& v, const variant<T...>& w);
-template<class... T>
- constexpr bool operator<(const variant<T...>& v, const variant<T...>& w);
-template<class... T>
- constexpr bool operator>(const variant<T...>& v, const variant<T...>& w);
-template<class... T>
- constexpr bool operator<=(const variant<T...>& v, const variant<T...>& w);
-template<class... T>
- constexpr bool operator>=(const variant<T...>& v, const variant<T...>& w);
-
-// visit
-
-template<class F, class... V>
- constexpr /*see below*/ visit(F&& f, V&&... v);
-
-// monostate
-
-struct monostate {};
-
-constexpr bool operator==(monostate, monostate) noexcept { return true; }
-constexpr bool operator!=(monostate, monostate) noexcept { return false; }
-constexpr bool operator<(monostate, monostate) noexcept { return false; }
-constexpr bool operator>(monostate, monostate) noexcept { return false; }
-constexpr bool operator<=(monostate, monostate) noexcept { return true; }
-constexpr bool operator>=(monostate, monostate) noexcept { return true; }
-
-// swap
-
-template<class... T>
- void swap(variant<T...>& v, variant<T...>& w) noexcept( /*see below*/ );
-
-// bad_variant_access
-
-class bad_variant_access;
-
-} // namespace variant2
-} // namespace boost
-variant
-namespace boost {
-namespace variant2 {
-
-template<class... T> class variant
-{
-public:
-
- // constructors
-
- constexpr variant() noexcept( /*see below*/ );
-
- constexpr variant( variant const & r ) noexcept( /*see below*/ );
- constexpr variant( variant&& r ) noexcept( /*see below*/ );
-
- template<class U>
- constexpr variant( U&& u ) noexcept( /*see below*/ );
-
- template<class U, class... A>
- constexpr explicit variant( in_place_type_t<U>, A&&... a );
- template<class U, class V, class... A>
- constexpr explicit variant( in_place_type_t<U>,
- std::initializer_list<V> il, A&&... a );
-
- template<size_t I, class... A>
- constexpr explicit variant( in_place_index_t<I>, A&&... a );
- template<size_t I, class V, class... A>
- constexpr explicit variant( in_place_index_t<I>,
- std::initializer_list<V> il, A&&... a );
-
- // destructor
-
- ~variant();
-
- // assignment
-
- constexpr variant& operator=( variant const & r ) noexcept( /*see below*/ );
- constexpr variant& operator=( variant&& r ) noexcept( /*see below*/ );
-
- template<class U> constexpr variant& operator=( U&& u ) noexcept( /*see below*/ );
-
- // modifiers
-
- template<class U, class... A>
- constexpr U& emplace( A&&... a );
- template<class U, class V, class... A>
- constexpr U& emplace( std::initializer_list<V> il, A&&... a );
-
- template<size_t I, class... A>
- constexpr variant_alternative_t<I, variant<T...>>&
- emplace( A&&... a );
- template<size_t I, class V, class... A>
- constexpr variant_alternative_t<I, variant<T...>>&
- emplace( std::initializer_list<V> il, A&&... a );
-
- // value status
-
- constexpr bool valueless_by_exception() const noexcept;
- constexpr size_t index() const noexcept;
-
- // swap
-
- void swap( variant& r ) noexcept( /*see below*/ );
-
- // converting constructors (extension)
-
- template<class... U> variant( variant<U...> const& r )
- noexcept( /*see below*/ );
-
- template<class... U> variant( variant<U...>&& r )
- noexcept( /*see below*/ );
-
- // subset (extension)
-
- template<class... U> constexpr variant<U...> subset() & ;
- template<class... U> constexpr variant<U...> subset() && ;
- template<class... U> constexpr variant<U...> subset() const& ;
- template<class... U> constexpr variant<U...> subset() const&& ;
-};
-
-} // namespace variant2
-} // namespace boost
-In the descriptions that follow, let i
be in the range [0, sizeof…(T))
,
-and Ti
be the i
-th type in T…
.
Constructors
-constexpr variant() noexcept( std::is_nothrow_default_constructible_v<T0> );
--
-
-
-
---
-
-
- Effects: -
-
-
Constructs a
-variant
holding a value-initialized value of -typeT0
.
- - Ensures: -
-
-
-index() == 0
.
- - Throws: -
-
-
Any exception thrown by the value-initialization of
-T0
.
- - Remarks: -
-
-
This function does not participate in overload resolution unless -
-std::is_default_constructible_v<T0>
istrue
.
-
-
constexpr variant( variant const & w )
- noexcept( mp_all<std::is_nothrow_copy_constructible<T>...>::value );
--
-
-
-
---
-
-
- Effects: -
-
-
Initializes the variant to hold the same alternative and value as -
-w
.
- - Throws: -
-
-
Any exception thrown by the initialization of the contained value.
-
- - Remarks: -
-
-
This function does not participate in overload resolution unless -
-std::is_copy_constructible_v<Ti>
istrue
for alli
.
-
-
constexpr variant( variant&& w )
- noexcept( mp_all<std::is_nothrow_move_constructible<T>...>::value );
--
-
-
-
---
-
-
- Effects: -
-
-
Initializes the variant to hold the same alternative and value as -
-w
.
- - Throws: -
-
-
Any exception thrown by the move-initialization of the contained -value.
-
- - Remarks: -
-
-
This function does not participate in overload resolution unless -
-std::is_move_constructible_v<Ti>
istrue
for alli
.
-
-
template<class U> constexpr variant( U&& u ) noexcept(/*see below*/);
--
-
-
-
---
Let
-Tj
be a type that is determined as follows: build an imaginary function -FUN(Ti)
for each alternative typeTi
. The overloadFUN(Tj)
selected by -overload resolution for the expressionFUN(std::forward<U>(u))
defines the -alternativeTj
which is the type of the contained value after construction.---
-
- Effects: -
-
-
Initializes
-*this
to hold the alternative typeTj
and -initializes the contained value fromstd::forward<U>(u)
.
- - Ensures: -
-
-
-holds_alternative<Tj>(*this)
.
- - Throws: -
-
-
Any exception thrown by the initialization of the contained value.
-
- - Remarks: -
-
-
The expression inside
-noexcept
is equivalent to -std::is_nothrow_constructible_v<Tj, U>
. This function does not participate in -overload resolution unless---
-
-
-
-sizeof…(T)
is nonzero,
- -
-
-std::is_same_v<std::remove_cvref_t<U>, variant>
isfalse
,
- -
-
-std::remove_cvref_t<U>
is neither a specialization ofin_place_type_t
nor a -specialization ofin_place_index_t
,
- -
-
-std::is_constructible_v<Tj, U>
istrue
, and
- -
-
the expression
-FUN(std::forward<U>(u))
is well-formed.
-
- -
-
-
template<class U, class... A>
- constexpr explicit variant( in_place_type_t<U>, A&&... a );
--
-
-
-
---
-
-
- Effects: -
-
-
Initializes the contained value of type
-U
with the arguments -std::forward<A>(a)…
.
- - Ensures: -
-
-
-holds_alternative<U>(*this)
.
- - Throws: -
-
-
Any exception thrown by the initialization of the contained value.
-
- - Remarks: -
-
-
This function does not participate in overload resolution unless -there is exactly one occurrence of
-U
inT…
and -std::is_constructible_v<U, A…>
is true.
-
-
template<class U, class V, class... A>
- constexpr explicit variant( in_place_type_t<U>, std::initializer_list<V> il,
- A&&... a );
--
-
-
-
---
-
-
- Effects: -
-
-
Initializes the contained value of type
-U
with the argumentsil
, -std::forward<A>(a)…
.
- - Ensures: -
-
-
-holds_alternative<U>(*this)
.
- - Throws: -
-
-
Any exception thrown by the initialization of the contained value.
-
- - Remarks: -
-
-
This function does not participate in overload resolution unless -there is exactly one occurrence of
-U
inT…
and -std::is_constructible_v<U, initializer_list<V>&, A…>
istrue
.
-
-
template<size_t I, class... A>
- constexpr explicit variant( in_place_index_t<I>, A&&... a );
--
-
-
-
---
-
-
- Effects: -
-
-
Initializes the contained value of type
-TI
with the arguments -std::forward<A>(a)…
.
- - Ensures: -
-
-
-index() == I
.
- - Throws: -
-
-
Any exception thrown by the initialization of the contained value.
-
- - Remarks: -
-
-
This function does not participate in overload resolution unless -
-I < sizeof…(T)
andstd::is_constructible_v<TI, A…>
istrue
.
-
-
template<size_t I, class V, class... A>
- constexpr explicit variant( in_place_index_t<I>, std::initializer_list<V> il,
- A&&... a );
--
-
-
-
---
-
-
- Effects: -
-
-
Initializes the contained value of type
-TI
with the arguments -il
,std::forward<A>(a)…
.
- - Ensures: -
-
-
-index() == I
.
- - Throws: -
-
-
Any exception thrown by the initialization of the contained value.
-
- - Remarks: -
-
-
This function does not participate in overload resolution unless -
-I < sizeof…(T)
and -std::is_constructible_v<TI, initializer_list<V>&, A…>
istrue
.
-
-
Destructor
-~variant();
--
-
-
-
---
-
-
- Effects: -
-
-
Destroys the currently contained value.
-
-
-
Assignment
-constexpr variant& operator=( const variant& r )
- noexcept( mp_all<std::is_nothrow_copy_constructible<T>...>::value );
--
-
-
-
---
Let
-j
ber.index()
.---
-
- Effects: -
-
-
-emplace<j>(get<j>(r))
.
- - Returns: -
-
-
-*this
.
- - Ensures: -
-
-
-index() == r.index()
.
- - Remarks: -
-
-
This operator does not participate in overload resolution unless -
-std::is_copy_constructible_v<Ti> && std::is_copy_assignable_v<Ti>
is -true
for alli
.
-
-
constexpr variant& operator=( variant&& r )
- noexcept( mp_all<std::is_nothrow_move_constructible<T>...>::value );
--
-
-
-
---
Let
-j
ber.index()
.---
-
- Effects: -
-
-
-emplace<j>(get<j>(std::move(r)))
.
- - Returns: -
-
-
-*this
.
- - Ensures: -
-
-
-index() == r.index()
.
- - Remarks: -
-
-
This operator does not participate in overload resolution unless -
-std::is_move_constructible_v<Ti> && std::is_move_assignable_v<Ti>
is -true
for alli
.
-
-
template<class U> constexpr variant& operator=( U&& u )
- noexcept( /*see below*/ );
--
-
-
-
---
Let
-Tj
be a type that is determined as follows: build an imaginary function -FUN(Ti)
for each alternative typeTi
. The overloadFUN(Tj)
selected by -overload resolution for the expressionFUN(std::forward<U>(u))
defines the -alternativeTj
which is the type of the contained value after construction.---
-
- Effects: -
-
-
-emplace<j>(std::forward<U>(u))
.
- - Returns: -
-
-
-*this
.
- - Ensures: -
-
-
-index() == j
.
- - Remarks: -
-
-
The expression inside
-noexcept
isstd::is_nothrow_constructible_v<Tj, U&&>
. -This operator does not participate in overload resolution unless---
-
-
-
-std::is_same_v<std::remove_cvref_t<T>, variant>
isfalse
,
- -
-
-std::is_constructible_v<Tj, U&&> && std::is_assignable_v<Tj&, U&&>
is -true
, and
- -
-
the expression
-FUN(std::forward<U>(u))
(withFUN
being the -above-mentioned set of imaginary functions) is well-formed.
-
- -
-
-
Modifiers
-template<class U, class... A>
- constexpr U& emplace( A&&... a );
--
-
-
-
---
Let
-I
be the zero-based index ofU
inT…
.---
-
- Effects: -
-
-
Equivalent to:
-return emplace<I>(std::forward<A>(a)…);
- - Remarks: -
-
-
This function shall not participate in overload resolution unless -
-std::is_constructible_v<U, A&&…>
istrue
andU
occurs exactly once -inT…
.
-
-
template<class U, class V, class... A>
- constexpr U& emplace( std::initializer_list<V> il, A&&... a );
--
-
-
-
---
Let
-I
be the zero-based index ofU
inT…
.---
-
- Effects: -
-
-
Equivalent to:
-return emplace<I>(il, std::forward<A>(a)…);
- - Remarks: -
-
-
This function shall not participate in overload resolution unless -
-std::is_constructible_v<U, std::initializer_list<V>&, A&&…>
istrue
-andU
occurs exactly once inT…
.
-
-
template<size_t I, class... A>
- constexpr variant_alternative_t<I, variant<T...>>&
- emplace( A&&... a );
--
-
-
-
---
-
-
- Requires: -
-
-
-I < sizeof…(T)
.
- - Effects: -
-
-
Destroys the currently contained value, then initializes a new contained -value as if using the expression
-Ti(std::forward<A>(a)…)
.
- - Ensures: -
-
-
-index() == I
.
- - Returns: -
-
-
A reference to the new contained value.
-
- - Throws: -
-
-
Nothing unless the initialization of the new contained value throws.
-
- - Exception Safety: -
-
-
Strong. On exception, the contained value is unchanged.
-
- - Remarks: -
-
-
This function shall not participate in overload resolution unless -
-std::is_constructible_v<Ti, A&&…>
istrue
.
-
-
template<size_t I, class V, class... A>
- constexpr variant_alternative_t<I, variant<T...>>&
- emplace( std::initializer_list<V> il, A&&... a );
--
-
-
-
---
-
-
- Requires: -
-
-
-I < sizeof…(T)
.
- - Effects: -
-
-
Destroys the currently contained value, then initializes a new contained -value as if using the expression
-Ti(il, std::forward<A>(a)…)
.
- - Ensures: -
-
-
-index() == I
.
- - Returns: -
-
-
A reference to the new contained value.
-
- - Throws: -
-
-
Nothing unless the initialization of the new contained value throws.
-
- - Exception Safety: -
-
-
Strong. On exception, the contained value is unchanged.
-
- - Remarks: -
-
-
This function shall not participate in overload resolution unless -
-std::is_constructible_v<Ti, std::initializer_list<V>&, A&&…>
istrue
.
-
-
Value Status
-constexpr bool valueless_by_exception() const noexcept;
--
-
-
-
---
-
-
- Returns: -
-
-
-false
.
-
---
-- -- -Note--This function is provided purely for compatibility with -std::variant
. -
-
constexpr size_t index() const noexcept;
--
-
-
-
---
-
-
- Returns: -
-
-
The zero-based index of the active alternative.
-
-
-
Swap
-void swap( variant& r ) noexcept( mp_all<std::is_nothrow_move_constructible<T>...,
- is_nothrow_swappable<T>...>::value );
--
-
-
-
---
-
-
- Effects: -
-
---
-
-
-
-
If
-index() == r.index()
, callsswap(get<I>(*this), get<I>(r))
, -whereI
isindex()
.
- -
-
Otherwise, as if -
-variant tmp(std::move(*this)); *this = std::move(r); r = std::move(tmp);
-
- -
-
-
Converting Constructors (extension)
-template<class... U> variant( variant<U...> const& r )
- noexcept( mp_all<std::is_nothrow_copy_constructible<U>...>::value );
--
-
-
-
---
-
-
- Effects: -
-
-
Initializes the contained value from the contained value of
-r
.
- - Throws: -
-
-
Any exception thrown by the initialization of the contained value.
-
- - Remarks: -
-
-
This function does not participate in overload resolution unless -all types in
-U…
are inT…
and -std::is_copy_constructible_v<Ui>::value
istrue
for allUi
.
-
-
template<class... U> variant( variant<U...>&& r )
- noexcept( mp_all<std::is_nothrow_move_constructible<U>...>::value );
--
-
-
-
---
-
-
- Effects: -
-
-
Initializes the contained value from the contained value of -
-std::move(r)
.
- - Throws: -
-
-
Any exception thrown by the initialization of the contained value.
-
- - Remarks: -
-
-
This function does not participate in overload resolution unless -all types in
-U…
are inT…
and -std::is_move_constructible_v<Ui>::value
istrue
for allUi
.
-
-
Subset (extension)
-template<class... U> constexpr variant<U...> subset() & ;
-template<class... U> constexpr variant<U...> subset() const& ;
--
-
-
-
---
-
-
- Returns: -
-
-
A
-variant<U…>
whose contained value is copy-initialized from -the contained value of*this
and has the same type.
- - Throws: -
-
---
-
-
-
-
If the active alternative of
-*this
is not among the types inU…
, -bad_variant_access
.
- -
-
Otherwise, any exception thrown by the initialization of the contained value.
-
-
- -
-
- Remarks: -
-
-
This function does not participate in overload resolution unless -all types in
-U…
are inT…
and -std::is_copy_constructible_v<Ui>::value
istrue
for allUi
.
-
-
template<class... U> constexpr variant<U...> subset() && ;
-template<class... U> constexpr variant<U...> subset() const&& ;
--
-
-
-
---
-
-
- Returns: -
-
-
A
-variant<U…>
whose contained value is move-initialized from -the contained value of*this
and has the same type.
- - Throws: -
-
---
-
-
-
-
If the active alternative of
-*this
is not among the types inU…
, -bad_variant_access
.
- -
-
Otherwise, any exception thrown by the initialization of the contained value.
-
-
- -
-
- Remarks: -
-
-
This function does not participate in overload resolution unless -all types in
-U…
are inT…
and -std::is_move_constructible_v<Ui>::value
istrue
for allUi
.
-
-
variant_alternative
-template<size_t I, class T> struct variant_alternative<I, T const>;
-template<size_t I, class T> struct variant_alternative<I, T volatile>;
-template<size_t I, class T> struct variant_alternative<I, T const volatile>;
-template<size_t I, class T> struct variant_alternative<I, T&>; // extension
-template<size_t I, class T> struct variant_alternative<I, T&&>; // extension
--
-
-
-
-------
If
-typename variant_alternative<I, T>::type
exists and isU
,---
-
-
-
-variant_alternative<I, T const>::type
isU const
;
- -
-
-variant_alternative<I, T volatile>::type
isU volatile
;
- -
-
-variant_alternative<I, T const volatile>::type
isU const volatile
.
- -
-
-variant_alternative<I, T&>::type
isU&
.
- -
-
-variant_alternative<I, T&&>::type
isU&&
.
-
--Otherwise, these structs have no member
-type
.
- -
-
template<size_t I, class... T>
- struct variant_alternative<I, variant<T...>>;
--
-
-
-
---
When
-I < sizeof…(T)
, the nested typetype
is an alias for theI
-th -(zero-based) type inT…
. Otherwise, there is no membertype
.
-
holds_alternative
-template<class U, class... T>
- constexpr bool holds_alternative(const variant<T...>& v) noexcept;
--
-
-
-
---
-
-
- Requires: -
-
-
The type
-U
occurs exactly once inT…
. Otherwise, the -program is ill-formed.
- - Returns: -
-
-
-true
ifindex()
is equal to the zero-based index ofU
-inT…
.
-
-
get
-template<size_t I, class... T>
- constexpr variant_alternative_t<I, variant<T...>>&
- get(variant<T...>& v);
-template<size_t I, class... T>
- constexpr variant_alternative_t<I, variant<T...>>&&
- get(variant<T...>&& v);
-template<size_t I, class... T>
- constexpr const variant_alternative_t<I, variant<T...>>&
- get(const variant<T...>& v);
-template<size_t I, class... T>
- constexpr const variant_alternative_t<I, variant<T...>>&&
- get(const variant<T...>&& v);
--
-
-
-
---
-
-
- Effects: -
-
-
If
-v.index()
isI
, returns a reference to the object stored in -the variant. Otherwise, throwsbad_variant_access
.
- - Remarks: -
-
-
These functions do not participate in overload resolution -unless
-I
<sizeof…(T)
.
-
-
template<class U, class... T>
- constexpr U& get(variant<T...>& v);
-template<class U, class... T>
- constexpr U&& get(variant<T...>&& v);
-template<class U, class... T>
- constexpr const U& get(const variant<T...>& v);
-template<class U, class... T>
- constexpr const U&& get(const variant<T...>&& v);
--
-
-
-
---
-
-
- Requires: -
-
-
The type
-U
occurs exactly once inT…
. Otherwise, the -program is ill-formed.
- - Effects: -
-
-
If
-v
holds a value of typeU
, returns a reference to that value. -Otherwise, throwsbad_variant_access
.
-
-
get_if
-template<size_t I, class... T>
- constexpr add_pointer_t<variant_alternative_t<I, variant<T...>>>
- get_if(variant<T...>* v) noexcept;
-template<size_t I, class... T>
- constexpr add_pointer_t<const variant_alternative_t<I, variant<T...>>>
- get_if(const variant<T...>* v) noexcept;
--
-
-
-
---
-
-
- Effects: -
-
-
A pointer to the value stored in the variant, if -
-v != nullptr && v->index() == I
. Otherwise,nullptr
.
- - Remarks: -
-
-
These functions do not participate in overload resolution -unless
-I
<sizeof…(T)
.
-
-
template<class U, class... T>
- constexpr add_pointer_t<U>
- get_if(variant<T...>* v) noexcept;
-template<class U, class... T>
- constexpr add_pointer_t<const U>
- get_if(const variant<T...>* v) noexcept;
--
-
-
-
---
-
-
- Requires: -
-
-
The type
-U
occurs exactly once inT…
. Otherwise, the -program is ill-formed.
- - Effects: -
-
-
Equivalent to:
-return get_if<I>(v);
withI
being -the zero-based index ofU
inT…
.
-
-
Relational Operators
-template<class... T>
- constexpr bool operator==(const variant<T...>& v, const variant<T...>& w);
--
-
-
-
---
-
-
- Returns: -
-
-
-v.index() == w.index() && get<I>(v) == get<I>(w)
, whereI
-isv.index()
.
-
-
template<class... T>
- constexpr bool operator!=(const variant<T...>& v, const variant<T...>& w);
--
-
-
-
---
-
-
- Returns: -
-
-
-!(v == w)
.
-
-
template<class... T>
- constexpr bool operator<(const variant<T...>& v, const variant<T...>& w);
--
-
-
-
---
-
-
- Returns: -
-
-
-v.index() < w.index() || (v.index() == w.index() && get<I>(v) < get<I>(w))
, -whereI
isv.index()
.
-
-
template<class... T>
- constexpr bool operator>(const variant<T...>& v, const variant<T...>& w);
--
-
-
-
---
-
-
- Returns: -
-
-
-w < v
.
-
-
template<class... T>
- constexpr bool operator<=(const variant<T...>& v, const variant<T...>& w);
--
-
-
-
---
-
-
- Returns: -
-
-
-v.index() < w.index() || (v.index() == w.index() && get<I>(v) <= get<I>(w))
, -whereI
isv.index()
.
-
-
template<class... T>
- constexpr bool operator>=(const variant<T...>& v, const variant<T...>& w);
--
-
-
-
---
-
-
- Returns: -
-
-
-w <= v
.
-
-
visit
-template<class F, class... V>
- constexpr /*see below*/ visit(F&& f, V&&... v);
--
-
-
-
---
-
-
- Returns: -
-
-
-std::forward<F>(f)(get<I>(std::forward<V>(v))…)
, where -I…
isv.index()…
.
-
-
swap
-template<class... T>
- void swap(variant<T...>& v, variant<T...>& w) noexcept( /*see below*/ );
--
-
-
-
---
-
-
- Effects: -
-
-
Equivalent to
-v.swap(w)
.
-
-
bad_variant_access
-class bad_variant_access: public std::exception
-{
-public:
-
- bad_variant_access() noexcept = default;
-
- char const * what() const noexcept
- {
- return "bad_variant_access";
- }
-};
-Copyright and License
-This documentation is copyright 2018, 2019 Peter Dimov and is distributed under -the Boost Software License, Version 1.0.
-