| Document Number: | P0088R2, ISO/IEC JTC1 SC22 WG21 | 
| Audience: | LWG | 
| Date: | 2016-03-21 | 
| Author: | Axel Naumann (axel@cern.ch) | 
Variant is the very spice of life,
That gives it all its flavor.
- William Cowper's "The Task", or actually a variant thereof
C++17 needs a type-safe union:
Lets not make the same mistake we made with std::optional by putting this library into a TS. We waited three years where no substantial feedback or discussion occurred, and then moved it into the IS virtually unchanged. Meanwhile, the C++ community suffered, and we continue to suffer from lack of this essential vocabulary type in interfaces.
The implications of the consensus variant design are well understood and have been explored over several LEWG discussions, over a thousand emails, a joint LEWG/EWG session, and not to mention 12 years of experience with Boost and other libraries. The last major change made to the proposal was non-breaking and added exception throws where previously there was undefined behavior. Since then, all suggested modifications have been cosmetic, rehashes of older discussions, or would be handled just as well by defect resolutions.
The C++ community should not wait three years for a widely useful library that is already done, fits its purpose, and has had such extensive review. There is a low chance that we will regret including variant in C++17, but a high chance that we will regret omitting it.
This proposal attempts to apply the lessons learned from optional (1). It behaves as below:
variant<int, float> v, w;
v = 12;
int i = get<int>(v);
w = get<int>(v);
w = get<0>(v); // same effect as the previous line
w = v; // same effect as the previous line
get<double>(v); // ill formed
get<3>(v); // ill formed
try {
  get<float>(w); // will throw.
}
catch (bad_variant_access&) {}
The LEWG review in Urbana resulted in the following straw polls that motivated changes in this revision of the paper:
tuple-like interface instead of the collection of variant-specific functions, is_alternative etc.? SF=8 WF=5 N=2 WA=1 SA=0variant should be as constexpr as std::optionalvariant<int, int> and variant<int, const int>.In Lenexa, LEWG decided that variant should model a discriminated union.
variant<int, string> x = "abc";? SF=5 WF=4 N=1 WA=1 SA=0variant<string> == const char * and variant<const char *, string> == const char *? SF=0 WF=2 N=5 WA=3 SA=3variant<string> == variant<const char *>, and variant<A, B, C> == variant<X, Y, Z>? SF=0 WF=1 N=0 WA=4 SA=8variant<int, const int>, qualified types in general? SF=9 WF=4 N=1 WA=1 SA=1visit(VISITOR, var1, var2, var3, ...)? SF=0 WF=7 N=7 WA=1 SA=0visit(VISITOR, v1, v2)? SF=0 WF=1 N=10 WA=1 SA=3common_type: 12op()(), rest must convert to that: 1variant<return types>: 2variant<return types> if they're different, otherwise single return type: 0void * data()T* get<T>(variant<A, B, C> *) (a la any_cast)index() return -1 on empty? (The alternative is to make non-emptiness a precondition.) SF=4 WF=1 N=3 WA=1 SA=2variant::{visit,get} have preconditions that the variant not be empty? SF=4 WF=8 N=2 WA=0 SA=0This addressed items raised by LWG.
template <class T> variant operator=(T&&), using a hypothetical function taking the alternative types.visit(Visitor).valid() to !corrupted_by_exception() (14 votes, runner-up: 7 votes) that was later on changed to !valueless_by_exception() after a discussion and based on a poll on the LWG and LEWG email lists, with 32 responses.As requested by the LEWG review in Urbana, this revision
variant to be empty;void as alternatives behave;tuple for parameter pack operations; is_alternative does not yet exist as part of tuple and is thus kept;index() to return -1 (now also known is tuple_not_found) if !valid();Beyond these requests, this revision
variant, an alternative, and a different variant type;variant a regular type.variant now models a discriminated union.hash<variant<int>> can now return different values than hash<int> (and it should - presumably it should take the index() into account).template <size_t,...> get<I,...>(variant).is_alternative that is not strictly needed to make variant usable (LEWG feedback).std::swap() specialization; the default is just fine.visit() is now variadic.type_list; reduced probability of !valid() for copy assignment / construction.valid() a visible state for value extraction functions (get(), visit()).valid() precondition for copy / move construction from a variant.!v.valid(), make get<...>(v) and visit(v) throw.template <class T> variant::variant(T&&) and template <class T> variant::operator=(T&&), using a hypothetical function taking the alternative types.visit(Visitor).valid() to !valueless_by_exception(), following the strong recommendation from a L(E)WG poll.tuple_find: it was not relevant for using variant as that already provides index- and type-based accesses; it was a considerable fraction of the proposed wording; it warrants a dedicated design paper, should someone wish to have it.emplaced_... becomes in_place_...constexpr support: construction, accessors, destruction,tuple_not_found that got removed by mistake. Call it variant_npos.tuple_size to variant_size, tuple_element to variant_alternative to clarify that this is not tuple-like. This avoids a clash with structured binding. The committee seems to have changed its common mind regarding these templates, reverting LEWG's decision from the first revision.variant valueless_by_exception if an exception is thrown during emplace / construction; merely state that it might become valueless_by_exception.LEWG opted against introducing an explicit additional variant state, representing its invalid (and possibly empty, default constructed) state. This is meant to simplify the variant use: as getting a variant into the invalid state is sufficiently difficult, it was felt that there is no need to regularly check for a variant becoming invalid. This prevents all get<int>(v) calls from being protected by if (v.valid()).
Accessing an invalid variant's value is undefined behavior, whatever alternative is accessed.
The variant's invalid state needs to be visible: accessing its contents or visiting it will violate preconditions; users must be able to verify that a variant is not in this state.
When in the invalid state, index() returns variant_npos; variant provides valid() as a usability feature.
This usually does not need to be checked given how rare the invalid case is. It (generally) keeps a variant with N alternatives as an N-state type.
Default construction of a variant should be allowed, to increase usability for instance in containers. LEWG opted against a variant default-initialized into its invalid state, to make invalid variants really rare.
Instead, the variant can be initialized with the first alternative (similar to the behavior of initialization of a union) only if that is default constructible. For cases where this behavior should be explicit, and for cases where no such default constructible alternative exists, there is a separate type monostate that can be used as first alternative, to explicitly enable default construction.
No header called variant exists; testing for this header's existence is thus sufficient.
The insertions and deletions in this section describe the changes to the C++ Working Paper. Grayish background indicates proposed wording. Blue markup indicates changes to the previous revision, with insertions and deletions signaled in an obvious way.
3.2.1 Header <experimental/tuple> synopsis
#include <tuple>
namespace std {
namespace experimental {
inline namespace fundamentals_vXXXX {
  // See C++14 §20.4.2.5, tuple helper classes
  template <class T> constexpr size_t tuple_size_v
    = tuple_size<T>::value;
  // 3.2.2, tuple_find
  static constexpr size_t tuple_not_found = -1;
} // namespace fundamentals_vXXXX
} // namespace experimental
} // namespace std
The definition of tuple_not_found shall belong to [tuple.helper]; it shall not get a new section name.
  Insert a new element in Table 1, C++ library headers of [general.namespaces], named <.experimental/variant>
? Variants [variant]
?.1 In general [variant.general]
A variant object holds and manages the lifetime of a value. If the variant holds a value, that value's type has to be one of the template argument types given to
variant. These template arguments are called alternatives.?.2 Header
<synopsis [variant.synopsis]experimental/variant>namespace std {namespace experimental { inline namespace fundamentals_vXXXX {// ?.3, variant of value types template <class... Types> class variant; // ?.4, In-place construction template <class T> struct in_place_type_t{ explicit in_place_type_t() = default; }; template <class T> constexpr in_place_type_t<T> in_place_type{}; template <size_t I> struct in_place_index_t{ explicit in_place_index_t() = default; }; template <size_t I> constexpr in_place_index_t<I> in_place_index{}; // ?.5, Value access template <class T, class... Types> constexpr bool holds_alternative(const variant<Types...>&) noexcept; template <size_t I, class... Types> constexprtuple_element_tvariant_alternative_t<I, variant<Types...>>& get(variant<Types...>&); template <size_t I, class... Types> constexprtuple_element_tvariant_alternative_t<I, variant<Types...>>&& get(variant<Types...>&&); template <size_t I, class... Types> constexprtuple_element_tvariant_alternative_t<I, variant<Types...>> const& get(const variant<Types...>&); template <size_t I, class... Types> constexprtuple_element_tvariant_alternative_t<I, variant<Types...>> const&& get(const variant<Types...>&&); template <class T, class... Types> constexpr T& get(variant<Types...>&); template <class T, class... Types> constexpr T&& get(variant<Types...>&&); template <class T, class... Types> constexpr const T& get(const variant<Types...>&); template <class T, class... Types> constexpr const T&& get(const variant<Types...>&&); template <size_t I, class... Types> constexpr add_pointer_t<tuple_element_tvariant_alternative_t<I, variant<Types...>>> get_if(variant<Types...>*) noexcept; template <size_t I, class... Types> constexpr add_pointer_t<consttuple_element_tvariant_alternative_t<I, variant<Types...>>> get_if(const variant<Types...>*) noexcept; template <class T, class... Types> constexpr add_pointer_t<T> get_if(variant<Types...>*) noexcept; template <class T, class... Types> constexpr add_pointer_t<const T> get_if(const variant<Types...>*) noexcept; // ?.6, Relational operators template <class... Types> constexpr bool operator==(const variant<Types...>&, const variant<Types...>&); template <class... Types> constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&); template <class... Types> constexpr bool operator<(const variant<Types...>&, const variant<Types...>&); template <class... Types> constexpr bool operator>(const variant<Types...>&, const variant<Types...>&); template <class... Types> constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&); template <class... Types> constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&); // ?.7, Visitation template <class Visitor, class... Variants> constexpr see below visit(Visitor&&, Variants&&...); // ?.8, Class monostate struct monostate; // ?.9, monostate relational operators constexpr bool operator<(monostate, monostate) noexcept; constexpr bool operator>(monostate, monostate) noexcept; constexpr bool operator<=(monostate, monostate) noexcept; constexpr bool operator>=(monostate, monostate) noexcept; constexpr bool operator==(monostate, monostate) noexcept; constexpr bool operator!=(monostate, monostate) noexcept; // ?.10, Specialized algorithms template <class... Types> void swap(variant<Types...>&, variant<Types...>&) noexcept(see below); // ?.11, class bad_variant_access class bad_variant_access;} // namespace fundamentals_vXXXX } // namespace experimental// ?.12,tupleinterfacevariant helper classes template <class T> struct variant_size; // undefined template <class T> struct variant_size<const T>; template <class T> struct variant_size<volatile T>; template <class T> struct variant_size<const volatile T>; template <class T> constexpr size_t variant_size_v = variant_size<T>::value; template <class... Types> structtuple_sizevariant_size<variant<Types...>>; template <size_t I, class T> struct variant_alternative; // undefined template <size_t I, class T> struct variant_alternative<I, const T>; template <size_t I, class T> struct variant_alternative<I, volatile T>; template <size_t I, class T> struct variant_alternative<I, const volatile T>; template <size_t I, class T> using variant_alternative_t = typename variant_alternative<I, T>::type; template <size_t I, class... Types> structtuple_elementvariant_alternative<I, variant<Types...>>; static constexpr size_t variant_npos = -1; // ?.13, Hash support template <class T> struct hash; template <class... Types> struct hash<experimental::variant<Types...>>; template <> struct hash<experimental::monostate>; // ?.14, Allocator-related traits template <class T, class Alloc> struct uses_allocator; template <class... Types, class Alloc> struct uses_allocator<experimental::variant<Types...>, Alloc>; } // namespace stdIncluding this header also makes the following declarations from [tuple.helper] available:
template <class T> class tuple_size<const T>; template <class T> class tuple_size<volatile T>; template <class T> class tuple_size<const volatile T>; template <size_t I, class T> class tuple_element<I, const T>; template <size_t I, class T> class tuple_element<I, volatile T>; template <size_t I, class T> class tuple_element<I, const volatile T>; static constexpr size_t tuple_not_found = -1;?.3
variantof value types [variant.variant]namespace std {namespace experimental { inline namespace fundamentals_vXXXX {template <class... Types> class variant { public: // ?.3.1 Constructors constexpr variant() noexcept(see below); variant(const variant&)noexcept(see below); variant(variant&&) noexcept(see below); template <class T> constexpr variant(T&&) noexcept(see below); template <class T, class... Args> constexpr explicit variant(in_place_type_t<T>, Args&&...); template <class T, class U, class... Args> constexpr explicit variant(in_place_type_t<T>, initializer_list<U>, Args&&...); template <size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&...); template <size_t I, class U, class... Args> constexpr explicit variant(in_place_index_t<I>, initializer_list<U>, Args&&...); // allocator-extended constructors template <class Alloc> variant(allocator_arg_t, const Alloc&); template <class Alloc> variant(allocator_arg_t, const Alloc&a, const variant&); template <class Alloc> variant(allocator_arg_t, const Alloc&a, variant&&); template <class Alloc, class T> variant(allocator_arg_t, const Alloc&, T&&); template <class Alloc, class T, class... Args> variant(allocator_arg_t, const Alloc&, in_place_type_t<T>, Args&&...); template <class Alloc, class T, class U, class... Args> variant(allocator_arg_t, const Alloc&, in_place_type_t<T>, initializer_list<U>, Args&&...); template <class Alloc, size_t I, class... Args> variant(allocator_arg_t, const Alloc&, in_place_index_t<I>, Args&&...); template <class Alloc, size_t I, class U, class... Args> variant(allocator_arg_t, const Alloc&, in_place_index_t<I>, initializer_list<U>, Args&&...); // ?.3.2, Destructor ~variant(); // ?.3.3, Assignment variant& operator=(const variant&); variant& operator=(variant&&) noexcept(see below); template <class T> variant& operator=(T&&) noexcept(see below); // ?.3.4, Modifiers template <class T, class... Args> void emplace(Args&&...); template <class T, class U, class... Args> void emplace(initializer_list<U>, Args&&...); template <size_t I, class... Args> void emplace(Args&&...); template <size_t I, class U, class... Args> void emplace(initializer_list<U>, Args&&...); // ?.3.5, Value status constexpr bool valueless_by_exception() const noexcept; constexpr size_t index() const noexcept; // ?.3.6, Swap void swap(variant&) noexcept(see below);private: aligned_storage<Unspecified>::type storage; // exposition only size_t value_type_index; // exposition only};} // namespace fundamentals_vXXXX } // namespace experimental} // namespace stdAny instance of
variantat any given time either holds a value of one of its alternative types, or it holds no value. When an instance ofvariantholds a value of alternative typeT, it means that a value of typeT, referred to as thevariantobject's contained value, is allocated within the storage of thevariantobject. Implementations are not permitted to use additional storage, such as dynamic memory, to allocate the contained value. The contained value shall be allocated in a region of thevariantstorage suitably aligned for all types inTypes.... It is implementation defined whether over-aligned types are supported.All types in
Types...shall be (possibly cv-qualified) object types, (possibly cv-qualified) void, or references. [Note: Implementations could decide to store references in areference_wrapper. — end note]?.3.1 Constructors [variant.ctor]
In the descriptions that follow, let
ibe in the range[0,sizeof...(Types)), andT_ibe theith type inTypes....
constexpr variant() noexcept(see below);
- Effects:
- Constructs a
variantholding a value-initialized value of typeT_0.- Postconditions:
valueless_by_exception()isfalseandindex()is0.- Throws:
- Any exception thrown by the value initialization of
T_0.- Remarks:
- This function shall be
constexprif and only if the value initialization of the alternative typeT_0would satisfy the requirements for aconstexprfunction. The expression insidenoexceptis equivalent tois_nothrow_default_constructible_v<T_0>. This function shall not participate in overload resolution unlessis_default_constructible_v<T_0>istrue. [Note: see also classmonostate. — end note]
variant(const variant& w);
- Effects:
- If
wholds a value, initializes thevariantto hold the same alternative aswand direct-initializes the contained valuefrom the value contained inwithwget<j>(w), wherejisw.index(). Otherwise, initializes thevariantto not hold a value.- Throws:
- Any exception thrown by direct-initializing any
T_ifor alli.- Remarks:
- This function shall not participate in overload resolution unless
is_copy_constructible_v<T_i>istruefor alli.
variant(variant&& w) noexcept(see below);
- Effects:
- If
wholds a value, initializes thevariantto hold the same alternative aswand direct-initializes the contained value withstd::forward<T_j>(get<j>(w))get<j>(std::move(w)), wherejisw.index(). Otherwise, initializes thevariantto not hold a value.- Throws:
- Any exception thrown by move-constructing any
T_ifor alli.- Remarks:
- The expression inside
noexceptis equivalent to the logical AND ofis_nothrow_move_constructible_v<T_i>for alli. This function shall not participate in overload resolution unlessis_move_constructible_v<T_i>istruefor alli.Let
template <class T> constexpr variant(T&& t) noexcept(see below);T_jbe a type that is determined as follows: build an imaginary functionFUN(T_i)for each alternative typeT_i. The overloadFUN(T_j)selected by regular overload resolution for the expressionFUN(std::forward<T>(t))defines the alternativeT_jwhich is the type of the contained value after construction.
Requires:
Builds an imaginary functionFUN(T_i)for each alternative typeT_i. The expressionFUN(std::forward<T>(t))must be valid according to regular overload resolution, otherwise the program is ill-formed. The selected functionFUN(T_j)defines the alternativeT_jthat will be activated by the call to this constructorwhich is the type of the contained value after construction. [Note:is ill-formed, as both alternative types have an equally viable constructor for the argument. — end note]variant<string, string> v("abc");- Effects:
- Initializes
*thisto hold the alternative typeT_jas selected by the imaginary function overload resolution described above,and direct-initializes the contained value as if direct-non-list-initializing it withstd::forward<T>(t).- Postconditions:
holds_alternative<T_j>(*this)istrue, with.T_jselected by the imaginary function overload resolution described above- Throws:
- Any exception thrown by the initialization of the selected alternative
T_j.- Remarks:
- This function shall not participate in overload resolution unless
is_same_v<decay_t<T>, variant>isfalse, unlessis_constructible_v<T_j, T&&>istrue, and unless the expressionFUN(std::forward<T>(t))(withFUNbeing the above-mentioned set of imaginary functions) isvalid according to regular overload resolutionwell formed. [Note:is ill-formed, as both alternative types have an equally viable constructor for the argument. — end note] The expression insidevariant<string, string> v("abc");noexceptis equivalent tois_nothrow_constructible_v<T_j, T&&>. IfT_j's selected constructor is aconstexprconstructor, this constructor shall be aconstexprconstructor.
template <class T, class... Args> constexpr explicit variant(in_place_type_t<T>, Args&&... args);
- Effects:
- Initializes the contained value
as if constructing an objectof typeTwith the argumentsstd::forward<Args>(args)....- Postcondition:
holds_alternative<T>(*this)istrue.- Throws:
- Any exception thrown by calling the selected constructor of
T.- Remarks:
- This function shall not participate in overload resolution unless there is exactly one occurrence of
TinTypes...andis_constructible_v<T, Args&&...>istrue. IfT's selected constructor is aconstexprconstructor, this constructor shall be aconstexprconstructor.
template <class T, class U, class... Args> constexpr explicit variant(in_place_type_t<T>, initializer_list<U> il, Args&&... args);
- Effects:
- Initializes the contained value as if constructing an object of type
Twith the argumentsil, std::forward<Args>(args)....- Postcondition:
holds_alternative<T>(*this)istrue.- Throws:
- Any exception thrown by calling the selected constructor of
T.- Remarks:
- This function shall not participate in overload resolution unless there is exactly one occurrences of
TinTypes...andis_constructible_v<T, initializer_list<U>&, Args&&...>istrue. IfT's selected constructor is aconstexprconstructor, this constructor shall be aconstexprconstructor.
template <size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&... args);
- Effects:
- Initializes the contained value as if constructing an object of type
T_Iwith the argumentsstd::forward<Args>(args)....- Postcondition:
index()isI.- Throws:
- Any exception thrown by calling the selected constructor of
T_I.- Remarks:
- This function shall not participate in overload resolution unless
Iis less thansizeof...(Types)andis_constructible_v<T_I, Args&&...>istrue. IfT_I's selected constructor is aconstexprconstructor, this constructor shall be aconstexprconstructor.
template <size_t I, class U, class... Args> constexpr explicit variant(in_place_index_t<I>, initializer_list<U> il, Args&&... args);
- Effects:
- Initializes the contained value as if constructing an object of type
T_Iwith the argumentsil, std::forward<Args>(args)....- Postcondition:
index()isI.- Remarks:
- This function shall not participate in overload resolution unless
Iis less thansizeof...(Types)andis_constructible_v<T_I, initializer_list<U>&, Args&&...>istrue. IfT_I's selected constructor is aconstexprconstructor, this constructor shall be aconstexprconstructor.
// allocator-extended constructors
template <class Alloc>
variant(allocator_arg_t, const Alloc& a);
template <class Alloc>
variant(allocator_arg_t, const Alloc& a, const variant& v);
template <class Alloc>
variant(allocator_arg_t, const Alloc& a, variant&& v);
template <class Alloc, class T>
variant(allocator_arg_t, const Alloc& a, T&& t);
template <class Alloc, class T, class... Args>
variant(allocator_arg_t, const Alloc& a, in_place_type_t<T>, Args&&... args);
template <class Alloc, class T, class U, class... Args>
variant(allocator_arg_t, const Alloc& a, in_place_type_t<T>, initializer_list<U> il, Args&&... args);
template <class Alloc, size_t I, class... Args>
variant(allocator_arg_t, const Alloc& a, in_place_index_t<I>, Args&&... args);
template <class Alloc, size_t I, class U, class... Args>
variant(allocator_arg_t, const Alloc& a, in_place_index_t<I>, initializer_list<U> il, Args&&... args);
- Requires:
Allocshall meet the requirements for anAllocator(17.6.3.5).- Effects:
- Equivalent to the preceding constructors except that the contained value is constructed with uses-allocator construction (20.7.7.2).
?.3.2 Destructor [variant.dtor]
~variant();
- Effects:
- If
valueless_by_exception()isfalse, destroys the currently contained value.- Remarks:
- If
is_trivially_destructible_v<T_i> == truefor allT_ithen this destructor shall be a trivial destructor.?.3.3 Assignment [variant.assign]
variant& operator=(const variant& rhs);
- Effects:
- If neither
*thisnorrhsholds a value, there is no effect. Otherwise- if
*thisholds a value butrhsdoes not, destroys the value contained in*thisand sets*thisto not hold a value. Otherwise,- if
index() == rhs.index(), assigns the value contained inrhsto the value contained in*this. Otherwise,- copies the value contained in
rhsto a temporary, then destroys any value contained in*this. Sets*thisto hold the same alternative index asrhsand initializes the value contained in*thisas if direct-non-list-initializing an object of typeT_jwithstd::forward<T_j>(TMP), withTMPbeing the temporary andjbeingrhs.index().
- If an exception is thrown during the call to
T_j's copy assignment, the state of the contained value is as defined by the exception safety guarantee ofT_j's copy assignment;index()will bej.- If an exception is thrown during the call to
T_j's copy constructor (withjbeingrhs.index()),*thiswill remain unchanged.- If an exception is thrown during the call to
T_j's move constructor, thevariantwill hold no value.- Returns:
*this.- Postconditions:
index() == rhs.index()- Remarks:
- This function shall not participate in overload resolution unless
is_copy_constructible_v<T_i> && is_move_constructible_v<T_i> && is_copy_assignable_v<T_i>istruefor alli.
- If an exception is thrown during the call to
T_j's copy assignment, the state of the contained value is as defined by the exception safety guarantee ofT_j's copy assignment;index()will bej.- If an exception is thrown during the call to
T_j's copy construction (withjbeingrhs.index()),*thiswill remain unchanged.- If an exception is thrown during the call to
T_j's move construction, thevariantwill hold no value.
variant& operator=(variant&& rhs) noexcept(see below);
- Effects:
- If neither
*thisnorrhsholds a value, there is no effect. Otherwise- if
*thisholds a value butrhsdoes not, destroys the value contained in*thisand sets*thisto not hold a value. Otherwise,- if
index() == rhs.index(), assignsget<j>(std::move(rhs)) to the value contained instd::forward<T_j>(get<j>(rhs))*this, withjbeingindex(). Otherwise,- destroys any value contained in
*this. Sets*thisto hold the same alternative index asrhsand initializes the value contained in*thisas if direct-non-list-initializing an object of typeT_jwithget<j>(rhs) withstd::forward<T_j>(get<j>(rhs))jbeingrhs.index().
If an exception is thrown during the call toT_j's move constructorion (withjbeingrhs.index()), thevariantwill hold no value. If an exception is thrown during the call toT_j's move assignment, the state of the contained value is as defined by the exception safety guarantee ofT_j's move assignment;index()will bej.- Returns:
*this.- Remarks:
- This function shall not participate in overload resolution unless
is_move_constructible_v<T_i> && is_move_assignable_v<T_i>istruefor alli. The expression insidenoexceptis equivalent to:is_nothrow_move_constructible_v<T_i> && is_nothrow_move_assignable_v<T_i>for alli. If an exception is thrown during the call toT_j's move constructorion (withjbeingrhs.index()), thevariantwill hold no value. If an exception is thrown during the call toT_j's move assignment, the state of the contained value is as defined by the exception safety guarantee ofT_j's move assignment;index()will bej.Let
template <class T> variant& operator=(T&& t) noexcept(see below);T_jbe a type that is determined as follows: build an imaginary functionFUN(T_i)for each alternative typeT_i. The overloadFUN(T_j)selected by regular overload resolution for the expressionFUN(std::forward<T>(t))defines the alternativeT_jwhich is the type of the contained value after assignment.
Requires:
Builds an imaginary functionFUN(T_i)for each alternative typeT_i. The expressionFUN(std::forward<T>(t))must be valid according to regular overload resolution, otherwise the program is ill-formed. The selected functionFUN(T_j)defines the alternativeT_jthat will be activated by the assignmentwhich is the type of the contained value after assignment. [Note:is ill-formed, as both alternative types have an equally viable constructor for the argument. — end note]variant<string, string> v; v = "abc";- Effects:
No effect ifIfdecay_t<T>(t)isvoid.*thisholds aT_j, assignsstd::forward<T>(t)to the value contained in*this. Otherwise, destroys any value contained in*this, sets*thisto hold the alternative typeT_jas selected by the imaginary function overload resolution described above, and direct-initializes the contained value as if direct-non-list-initializing it withstd::forward<T>(t).
If an exception is thrown during the assignment ofstd::forward<T>(t)to the value contained in*this, the state of the contained value andtare as defined by the exception safety guarantee of the assignment expression;valueless_by_exception()will befalse. If an exception is thrown during the initialization of the contained value, thevariantobjectwillmight not hold a value.- Postcondition:
holds_alternative<T_j>(*this)istrue, withT_jselected by the imaginary function overload resolution described above.- Returns:
*this.- Remarks:
- This function shall not participate in overload resolution unless
is_same_v<decay_t<T>, variant>isfalse, unlessis_assignable_v<T_j&, T&&> && is_constructible_v<T_j, T&&>istrue, and unless the expressionFUN(std::forward<T>(t))(withFUNbeing the above-mentioned set of imaginary functions) isvalid according to regular overload resolutionwell formed. [Note:is ill-formed, as both alternative types have an equally viable constructor for the argument. — end note] The expression insidevariant<string, string> v; v = "abc";noexceptis equivalent to:is_nothrow_assignable_v<T_ij&, T&&> && is_nothrow_constructible_v<T_ij, T&&>for all. If an exception is thrown during the assignment ofistd::forward<T>(t)to the value contained in*this, the state of the contained value andtare as defined by the exception safety guarantee of the assignment expression;valueless_by_exception()will befalse. If an exception is thrown during the initialization of the contained value, thevariantobjectwillmight not hold a value.?.3.4 Modifiers [variant.mod]
template <class T, class... Args> void emplace(Args&&... args);
- Effects:
Destroys the currently contained value ifEquivalent tovalueless_by_exception()isfalse. Then direct-initializes the contained value as if constructing a value of typeTwith the argumentsstd::forward<Args>(args)....emplace<I>(std::forward<Args>(args)...)whereIis the zero-based index ofTinTypes....If an exception is thrown during the initialization of the contained value, thevariantwillmight not hold a value.- Postcondition:
holds_alternative<T>(*this)istrue.
Throws:
Any exception thrown during the initialization of the contained value.- Remarks:
- This function shall not participate in overload resolution unless
is_constructible_v<T, Args&&...>istrue, andToccurs exactly once inTypes....
template <class T, class U, class... Args> void emplace(initializer_list<U> il, Args&&... args);
- Effects:
Destroys the currently contained value ifEquivalent tovalueless_by_exception()isfalse. Then direct-initializes the contained value as if constructing an object of typeTwith the argumentsil, std::forward<Args>(args)....emplace<I>(il, std::forward<Args>(args)...)whereIis the zero-based index ofTinTypes....If an exception is thrown during the initialization of the contained value, thevariantwillmight not hold a value.- Postcondition:
holds_alternative<T>(*this)istrue.
Throws:
Any exception thrown during the initialization of the contained value.- Remarks:
- This function shall not participate in overload resolution unless
is_constructible_v<T, initializer_list<U>&, Args&&...>istrue, andToccurs exactly once inTypes....
template <size_t I, class... Args> void emplace(Args&&... args);
- Requires:
I < sizeof...(Types)- Effects:
- Destroys the currently contained value if
valueless_by_exception()isfalse. Then direct-initializes the contained value as if constructing a value of typeT_Iwith the argumentsstd::forward<Args>(args)....If an exception is thrown during the initialization of the contained value, thevariantwillmight not hold a value.- Postcondition:
index()isI.- Throws:
- Any exception thrown during the initialization of the contained value.
- Remarks:
- This function shall not participate in overload resolution unless
is_constructible_v<T_I, Args&&...>istrue. If an exception is thrown during the initialization of the contained value, thevariantwillmight not hold a value.
template <size_t I, class U, class... Args> void emplace(initializer_list<U> il, Args&&... args);
- Requires:
I < sizeof...(Types)- Effects:
- Destroys the currently contained value if
valueless_by_exception()isfalse. Then direct-initializes the contained value as if constructing an object of typeT_Iwith the argumentsil, std::forward<Args>(args)....If an exception is thrown during the initialization of the contained value, thevariantwillmight not hold a value.- Postcondition:
index()isI.- Throws:
- Any exception thrown during the initialization of the contained value.
- Remarks:
- This function shall not participate in overload resolution unless
is_constructible_v<T_I, initializer_list<U>&, Args&&...>istrue. If an exception is thrown during the initialization of the contained value, thevariantwillmight not hold a value.?.3.5 Value status [variant.status]
constexpr bool valueless_by_exception() const noexcept;
- Effects:
- Returns
falseif and only if thevariantholds a valuewhether the. [Note: Avariantholds a value (returnsfalse)variantwillmight not hold a value if an exception is thrown during a type-changing assignment or emplacement. The latter means that even avariant<float,int>can becomevalueless_by_exception(), for instance by— end note]struct S { operator int() { throw 42; }}; variant<float, int> v{12.f}; v.emplace<1>(S());
constexpr size_t index() const noexcept;
- Effects:
- If
valueless_by_exception()istrue, returnsvariant_npos. Otherwise, returns the zero-based index of thecurrently activealternative of the contained value.?.3.6 Swap [variant.swap]
void swap(variant& rhs) noexcept(see below);
- Effects:
- if
valueless_by_exception() && rhs.valueless_by_exception()no effect. Otherwise,- if
index() == rhs.index(), callsswap(get<i>(*this), get<i>(rhs))whereiisindex(). Otherwise,- exchanges values of
rhsand*this.
If an exception is thrown during the call to functionswap(get<i>(*this), get<i>(rhs)), the states of the contained values of*thisand ofrhsare determined by the exception safety guarantee ofswapfor lvalues ofT_iwithibeingindex(). If an exception is thrown during the exchange of the values of*thisandrhs, the states of the values of*thisand ofrhsare determined by the exception safety guarantee ofvariant's move constructor and move assignment operator.- Throws:
- Any exception thrown by
swap(get<i>(*this), get<i>(rhs))withibeingindex()orvariant's move constructor and move assignment operator.- Remarks:
- This function shall not participate in overload resolution unless
is_swappable_v<T_i>istruefor alliall alternative types satisfy the. If an exception is thrown during the call to functionSwappablerequirements (17.6.3.2) with the corresponding alternative inrhsswap(get<i>(*this), get<i>(rhs)), the states of the contained values of*thisand ofrhsare determined by the exception safety guarantee ofswapfor lvalues ofT_iwithibeingindex(). If an exception is thrown during the exchange of the values of*thisandrhs, the states of the values of*thisand ofrhsare determined by the exception safety guarantee ofvariant's move constructor and move assignment operator.?.4 In-place construction [variant.in_place]
template <class T> struct in_place_type_t{ explicit in_place_type_t() = default; };
template <class T> constexpr in_place_type_t<T> in_place_type{};
template <size_t I> struct in_place_index_t{ explicit in_place_index_t() = default; };
template <size_t I> constexpr in_place_index_t<I> in_place_index{};
Template specializations of
in_place_type_tare empty structure types used as unique types to disambiguate constructor overloading. They signal (through the template parameter) the alternative to be constructed. Specifically,varianthas a constructor within_place_type_t<T>as the first argument followed by an argument pack; this indicates thatTshould be constructed in-place (as if by a call to a placement new expression) with the forwarded argument pack as parameters. If avariant'sTypes...has multiple occurrences ofT,in_place_index_tmust be used.Template specializations of
in_place_index_tare empty structure types used as unique types to disambiguate constructor overloading, and signaling (through the template parameter) the alternative to be constructed. Specifically,varianthas a constructor within_place_index_t<I>as the first argument followed by an argument pack; this indicates thatT_Ishould be constructed in-place (as if by a call to a placement new expression) with the forwarded argument pack as parameters.?.5 Value access [variant.get]
template <class T, class... Types> constexpr bool holds_alternative(const variant<Types...>& v) noexcept;
- Requires:
- The type
Toccurs exactly once inTypes.... Otherwise, the program is ill-formed.- Returns:
trueifindex()is equal to the zero-based index ofTinTypes....
template <size_t I, class... Types>
constexprtuple_element_tvariant_alternative_t<I, variant<Types...>>& get(variant<Types...>& v);
template <size_t I, class... Types>
constexprtuple_element_tvariant_alternative_t<I, variant<Types...>>&& get(variant<Types...>&& v);// Note A
template <size_t I, class... Types>
constexprtuple_element_tvariant_alternative_t<I, variant<Types...>> const& get(const variant<Types...>& v);// Note B
template <size_t I, class... Types>
constexprtuple_element_tvariant_alternative_t<I, variant<Types...>> const&& get(const variant<Types...>&& v);// Notes A and B
- Requires:
I < sizeof...(Types), andT_Iis not (possibly cv-qualified)void. Otherwise the program is ill-formed.- Effects:
- If
v.index()isI, returns a reference to the object stored in the variant. Otherwise, throws an exception of typebad_variant_access.
[Note A:
ifT_Iis some reference typeX&, the return type isX&, notX&&. However, if the element type is a non-reference typeT, the return type isT&&. — end note]
[Note B:
Constness is shallow. IfT_Iis some reference typeX&, the return type isX&, notconst X&. However, if the element type is non-reference typeT, the return type isconst T&. This is consistent with how constness is defined to work for member variables of reference type. — end note]
template <class T, class... Types> constexpr T& get(variant<Types...>& v);
template <class T, class... Types> constexpr T&& get(variant<Types...>&& v);
template <class T, class... Types> constexpr const T& get(const variant<Types...>& v);
template <class T, class... Types> constexpr const T&& get(const variant<Types...>&& v);
- Requires:
- The type
Toccurs exactly once inTypes..., andTis not (possibly cv-qualified)void. Otherwise, the program is ill-formed.- Effects:
- If
vholds a value of typeT, returns a reference to that value. Otherwise, throws an exception of typebad_variant_access.
template <size_t I, class... Types>
constexpr add_pointer_t<tuple_element_tvariant_alternative_t<I, variant<Types...>>> get_if(variant<Types...>* v) noexcept;
template <size_t I, class... Types>
constexpr add_pointer_t<consttuple_element_tvariant_alternative_t<I, variant<Types...>>> get_if(const variant<Types...>* v) noexcept;
- Requires:
I < sizeof...(Types)andT_Iis not (possibly cv-qualified)void; otherwise the program is ill-formed.- Returns:
- A pointer to the value stored in the variant, if
v != nullptrandv->index() == I. Otherwise, returnsnullptr.
template <class T, class... Types>
constexpr add_pointer_t<T> get_if(variant<Types...>* v) noexcept;
template <class T, class... Types>
constexpr add_pointer_t<const T> get_if(const variant<Types...>* v) noexcept;
- Requires:
- The type
Toccurs exactly once inTypes..., andTis not (possibly cv-qualified)void. Otherwise, the program is ill-formed.- Effects:
- Equivalent to
return get_if<i>(v)withibeing the zero-based index ofTinTypes....?.6 Relational operators [variant.relops]
template <class... Types> constexpr bool operator==(const variant<Types...>& v, const variant<Types...>& w);
- Requires:
get<i>(v) == get<i>(w)is a valid expression returning a type that is convertible tobool, for alli.- Effects:
- Equivalent to
return (v.valueless_by_exception() && w.valueless_by_exception()) || (v.index() == w.index() && get<i>(v) == get<i>(w))withibeingv.index(), otherwise.false
template <class... Types> constexpr bool operator!=(const variant<Types...>& v, const variant<Types...>& w);
ReturnsEffects:- Equivalent to
return!(v == w).
template <class... Types> constexpr bool operator<(const variant<Types...>& v, const variant<Types...>& w);
- Requires:
get<i>(v) < get<i>(w)is a valid expression returning a type that is convertible tobool, for alli.- Effects:
- Equivalent to
return (v.index() < w.index()) || (v.index() == w.index() && !v.valueless_by_exception() && get<i>(v) < get<i>(w))withibeingv.index(), otherwise.false
template <class... Types> constexpr bool operator>(const variant<Types...>& v, const variant<Types...>& w);
ReturnsEffects:- Equivalent to
returnw < v.
template <class... Types> constexpr bool operator<=(const variant<Types...>& v, const variant<Types...>& w);
ReturnsEffects:- Equivalent to
return!(v > w).
template <class... Types> constexpr bool operator>=(const variant<Types...>& v, const variant<Types...>& w);
ReturnsEffects:- Equivalent to
return!(v < w).?.7 Visitation [variant.visit]
template <class Visitor, class... Variants>
constexpr see below visit(Visitor&& vis, Variants&&... vars);
- Requires:
- The expression in the Effects element must be a valid expression of the same type and value category, for all combinations of alternative types of all variants. Otherwise, the program is ill-formed.
- Effects:
- Let
is...bevars.index().... ReturnsINVOKE(forward<Visitor>(vis), get<is>(forward<Variants>(vars))...);.- Remarks:
- The return type is the
common type of all possiblecommon_typeINVOKEexpressions of the Effects element.- Throws:
bad_variant_accessif anyvariantinvarsisvalueless_by_exception().- Complexity:
- For
sizeof...(Variants) <= 1, the invocation of the callable object must be implemented in constant time, i.e. it must not depend onsizeof...(Types). Forsizeof...(Variants) > 1, the invocation of the callable object has no complexity requirements.?.8 Class
monostate[variant.monostate]struct monostate{};The class
monostatecan serve as a first alternative type for avariantto make thevarianttype default constructible.?.9
monostaterelational operators [variant.monostate.relops]
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; }
constexpr bool operator==(monostate, monostate) noexcept { return true; }
constexpr bool operator!=(monostate, monostate) noexcept { return false; }
- [Note:
monostateobject have only a single state; they thus always compare equal.— end note]?.10 Specialized algorithms [variant.specalg]
template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);
- Effects:
CallsEquivalent tov.swap(w).- Remarks:
- The expression inside
noexceptis equivalent tonoexcept(v.swap(w)).?.11 Class
bad_variant_access[variant.bad_variant_access]class bad_variant_access : public exception { public: bad_variant_access() noexcept; virtual const char* what() const noexcept; };Objects of type
bad_variant_accessare thrown to report invalid accesses to the value of avariantobject.
bad_variant_access() noexcept;
- Effects:
- Constructs a
bad_variant_accessobject.
Postconditions:
what() returns an implementation-defined NTBS.
const char* what() const noexcept override;
- Returns:
- an implementation-defined NTBS.
?.12
tupleinterface to class templatevariantvarianthelper classes[variant.tuple][variant.helper]
template <class T> struct variant_size;
- Remarks:
- All specializations of
variant_size<T>shall meet theUnaryTypeTraitrequirements (20.10.1) with aBaseCharacteristicofintegral_constant<size_t, N>for someN.
template <class T> class variant_size<const T>;
template <class T> class variant_size<volatile T>;
template <class T> class variant_size<const volatile T>;
- Let
VSdenotevariant_size<T>of the cv-unqualified typeT. Then each of the three templates shall meet theUnaryTypeTraitrequirements (20.10.1) with aBaseCharacteristicofintegral_constant<size_t, VS::value>.
template <class... Types>
structtuple_sizevariant_size<variant<Types...>>
: integral_constant<size_t, sizeof...(Types)> { };
template <size_t I, class T> class variant_alternative<I, const T>;
template <size_t I, class T> class variant_alternative<I, volatile T>;
template <size_t I, class T> class variant_alternative<I, const volatile T>;
- Let
VAdenotevariant_alternative<I, T>of the cv-unqualified typeT. Then each of the three templates shall meet theTransformationTraitrequirements (20.10.1) with a member typedeftypethat names the following type:
- for the first specialization,
add_const_t<VA::type>,- for the second specialization,
add_volatile_t<VA::type>, and- for the third specialization,
add_cv_t<VA::type>.
tuple_elementvariant_alternative<I, variant<Types...>>::type
- Requires:
I < sizeof...(Types)- Value:
- The type
T_I.?.13 Hash support [variant.hash]
template <class... Types> struct hash<experimental::variant<Types...>>;The template specialization
hash<T>shall meet the requirements of class templatehash(C++14 §20.9.13) for allTinTypes.... The template specializationhash<shall meet the requirements of class templateexperimental::variant<Types...>>hash.
template <> struct hash<experimental::monostate>;The template specialization
hash<shall meet the requirements of class templateexperimental::monostate>hash.?.14 Allocator-related traits [variant.traits]
template <class... Types, class Alloc>
struct uses_allocator<experimental::variant<Types...>, Alloc> : true_type { };
- Requires:
Allocshall be anAllocator(17.6.3.5).- [Note:
- Specialization of this trait informs other library components that
variantcan be constructed with an allocator, even though it does not have a nestedallocator_type. — end note]
A variant has proven to be a useful tool. This paper proposes the necessary ingredients.
Thank you, Nevin ":-)" Liber, for bringing sanity to this proposal. Agustín K-ballo Bergé and Antony Polukhin provided very valuable feedback, criticism and suggestions. Thanks also to Vincenzo Innocente and Philippe Canal for their comments.
1. Working Draft, Technical Specification on C++ Extensions for Library Fundamentals. N4335
2. Improving pair and tuple, revision 2. N4064