Overview
This library implements a type-safe discriminated union (variant) type,
variant<T…>, that almost conforms to the C++17 Standard’s
std::variant<T…>. The
main differences between the two are:
-
variant<T…>does not have a valueless-by-exception state; -
A converting constructor from, e.g.
variant<int, float>tovariant<float, double, int>is provided as an extension; -
The reverse operation, going from
variant<float, double, int>tovariant<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 trivial when all contained types are trivial, as mandated by C++17.
To avoid the valueless-by-exception state, this implementation falls back to using double storage unless
-
one of the alternatives is the type
monostate, -
one of the alternatives has a nonthrowing default constructor, or
-
all the contained types are nothrow move constructible.
If the first two bullets don’t hold, but the third does, the variant uses
single storage, but emplace constructs a temporary and moves it into place
if the construction of the object can throw. In case this is undesirable, one
can force emplace into always constructing in-place by adding monostate as
one of the alternatives.
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
Constructors
constexpr variant() noexcept( std::is_nothrow_default_constructible_v<T0> );
-
- Effects:
-
Constructs a
variantholding 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>istruefor 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>istruefor alli.
template<class U> constexpr variant( U&& u ) noexcept(/*see below*/);
-
Let
Tjbe a type that is determined as follows: build an imaginary functionFUN(Ti)for each alternative typeTi. The overloadFUN(Tj)selected by overload resolution for the expressionFUN(std::forward<U>(u))defines the alternativeTjwhich is the type of the contained value after construction.- Effects:
-
Initializes
*thisto hold the alternative typeTjand 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
noexceptis equivalent tostd::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_tnor 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
Uwith the argumentsstd::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
UinT…andstd::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
Uwith 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
UinT…andstd::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
TIwith the argumentsstd::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
TIwith the argumentsil,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, 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>...,
std::is_nothrow_copy_assignable<T>...>::value );
-
Let
jber.index().- Effects:
-
-
If
index() == j, assigns the value contained inrto the value contained in*this. -
Otherwise, equivalent to
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>istruefor alli.
constexpr variant& operator=( variant&& r )
noexcept( mp_all<std::is_nothrow_move_constructible<T>...,
std::is_nothrow_move_assignable<T>...>::value );
-
Let
jber.index().- Effects:
-
-
If
index() == j, assigns the value contained instd::move(r)to the value contained in*this. -
Otherwise, equivalent to
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>istruefor alli.
template<class U> constexpr variant& operator=( U&& u )
noexcept( /*see below*/ );
-
Let
Tjbe a type that is determined as follows: build an imaginary functionFUN(Ti)for each alternative typeTi. The overloadFUN(Tj)selected by overload resolution for the expressionFUN(std::forward<U>(u))defines the alternativeTjwhich is the type of the contained value after construction.- Effects:
-
-
If
index() == j, assignsstd::forward<U>(u)to the value contained in*this. -
Otherwise, equivalent to
emplace<j>(std::forward<U>(u)).
-
- Returns:
-
*this. - Ensures:
-
index() == j. - Remarks:
-
The expression inside
noexceptisstd::is_nothrow_constructible_v<Tj, U> && std::is_nothrow_assignable_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>istrue, and -
the expression
FUN(std::forward<U>(u))(withFUNbeing the above-mentioned set of imaginary functions) is well-formed.
-
Modifiers
template<class U, class... A>
constexpr U& emplace( A&&... a );
-
Let
Ibe the zero-based index ofUinT….- 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…>istrueandUoccurs exactly once inT….
template<class U, class V, class... A>
constexpr U& emplace( std::initializer_list<V> il, A&&... a );
-
Let
Ibe the zero-based index ofUinT….- 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…>istrueandUoccurs 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.
- 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.
- 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.
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)), whereIisindex(). -
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…andstd::is_copy_constructible_v<Ui>::valueistruefor 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…andstd::is_move_constructible_v<Ui>::valueistruefor 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*thisand has the same type. - Throws:
-
-
If the active alternative of
*thisis 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…andstd::is_copy_constructible_v<Ui>::valueistruefor 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*thisand has the same type. - Throws:
-
-
If the active alternative of
*thisis 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…andstd::is_move_constructible_v<Ui>::valueistruefor 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>::typeexists and isU,-
variant_alternative<I, T const>::typeisU const; -
variant_alternative<I, T volatile>::typeisU volatile; -
variant_alternative<I, T const volatile>::typeisU const volatile. -
variant_alternative<I, T&>::typeisU&. -
variant_alternative<I, T&&>::typeisU&&.
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 typetypeis 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
Uoccurs exactly once inT…. Otherwise, the program is ill-formed. - Returns:
-
trueifindex()is equal to the zero-based index ofUinT….
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.
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
Uoccurs exactly once inT…. Otherwise, the program is ill-formed. - Effects:
-
If
vholds 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;
-
- Requires:
-
I < sizeof…(U). Otherwise, the program is ill-formed. - Effects:
-
A pointer to the value stored in the variant, if
v != nullptr && v->index() == I. Otherwise,nullptr.
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
Uoccurs exactly once inT…. Otherwise, the program is ill-formed. - Effects:
-
Equivalent to:
return get_if<I>(v);withIbeing the zero-based index ofUinT….
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), whereIisv.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)), whereIisv.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)), whereIisv.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))…), whereI…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.