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
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 functionFUN(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 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_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 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
U
inT…
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
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…
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
TI
with 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
TI
with 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
j
ber.index()
.- Effects:
-
-
If
index() == j
, assigns the value contained inr
to 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>
istrue
for alli
.
constexpr variant& operator=( variant&& r )
noexcept( mp_all<std::is_nothrow_move_constructible<T>...,
std::is_nothrow_move_assignable<T>...>::value );
-
Let
j
ber.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>
istrue
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 functionFUN(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:
-
-
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
noexcept
isstd::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))
(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.
- 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))
, 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…
andstd::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…
andstd::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…
andstd::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…
andstd::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
.
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;
-
- 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
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))…)
, 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.