Document Number: P0088R1, ISO/IEC JTC1 SC22 WG21
Audience:LWG
Date:2016-02-13
Author:Axel Naumann (axel@cern.ch)

Variant: a type-safe union (v6).

Table of Contents

 

Variant is the very spice of life,
That gives it all its flavor.
- William Cowper's "The Task", or actually a variant thereof

Introduction

C++ needs a type-safe union; here is a proposal. It 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&) {}

Results of the LEWG review in Urbana

The LEWG review in Urbana resulted in the following straw polls that motivated changes in this revision of the paper:

Results of the LEWG review in Lenexa

In Lenexa, LEWG decided that variant should model a discriminated union.

Results of the second LEWG review in Lenexa

Results of Evening Session review in Kona

Results of LEWG Session in Kona

This addressed items raised by LWG.

Differences to revision 1 (N4218)

As requested by the LEWG review in Urbana, this revision

Beyond these requests, this revision

Differences to revision 2 (N4450)

Differences to revision 3 (N4516)

Differences to revision 4 (N4542)

Differences to revision 5 (P0088R0)

Discussion

Additional empty state

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()).

Visibility of the Invalid State

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 tuple_not_found; 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.

Empty state and default construction

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.

Feature Test

No header called variant exists; testing for this header's existence is thus sufficient.

Proposed wording

The insertions and deletions in this section describe the changes to the Fundamentals TS. Grayish background indicates proposed wording.

Variant Objects

Insert a new element in Table 1, C++ library headers of [general.namespaces], named <experimental/variant>.

Insert a new section:

? Variants [variant]

?.1 In general [variant.general]

Variant objects hold and manage 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 <experimental/variant> synopsis [variant.synopsis]


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{};
  template <class T> constexpr in_place_type_t<T> in_place_type{};

  template <size_t I> struct in_place_index_t{};
  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>
    constexpr tuple_element_t<I, variant<Types...>>&
    get(variant<Types...>&);
  template <size_t I, class... Types>
    constexpr tuple_element_t<I, variant<Types...>>&&
    get(variant<Types...>&&);
  template <size_t I, class... Types>
    constexpr tuple_element_t<I, variant<Types...>> const&
    get(const variant<Types...>&);
  template <size_t I, class... Types>
    constexpr tuple_element_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_t<I, variant<Types...>>>
    get_if(variant<Types...>*) noexcept;
  template <size_t I, class... Types>
    constexpr add_pointer_t<const tuple_element_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, tuple interface
  template <class... Types>
    struct tuple_size<variant<Types...>>;
  template <size_t I, class... Types>
    struct tuple_element<I, variant<Types...>>;

  // ?.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 R, class Alloc>
  struct uses_allocator<experimental::variant<R>, Alloc>;
} // namespace std

Including this header also makes the following templates 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>;

?.3 variant of 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&&);

    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 std

Any instance of variant at any given time either holds a value of one of its alternative types, or it holds no value. When an instance of variant holds a value of alternative type T, it means that a value of type T, referred to as the variant object's contained value, is allocated within the storage of the variant object. 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 the variant storage suitably aligned for all types in Types. It is implementation defined whether over-aligned types are supported.

All types in Types shall be object types, possibly cv-qualified, possibly cv-qualified void, or references. [Note: Implementations could decide to store references in a reference_wrapper. — end note]

?.3.1 Constructors [variant.ctor]

In the descriptions that follow, let i be in the range [0,sizeof...(Types)), and T_i be the ith type in Types....

constexpr variant() noexcept(see below);

Effects:
Constructs a variant holding a value-initialized value of type T_0.
Postconditions:
valueless_by_exception() is false and index() is 0.
Throws:
Any exception thrown by the value initialization of T_0.
Remarks:
This function shall be noexcept if and only if the value initialization of the alternative type T_0 would satisfy the requirements for a constexpr function. The expression inside noexcept is equivalent to is_nothrow_default_constructible_v<T_0>. This function shall not participate in overload resolution unless is_default_constructible_v<T_0> is true. [Note: see also class monostate. — end note]

variant(const variant& w);

Effects:
If w holds a value, initializes the variant to hold the same alternative as w and direct-initializes the contained value from the value contained in w. Otherwise, initializes the variant to not hold a value.
Throws:
Any exception thrown by direct-initializing any T_i for all i.
Remarks:
This function shall not participate in overload resolution unless is_copy_constructible_v<T_i> is true for all i.

variant(variant&& w) noexcept(see below);

Effects:
If w holds a value, initializes the variant to hold the same alternative as w and direct-initializes the contained value with std::forward<T_j>(get<j>(w)), where j is w.index(). Otherwise, initializes the variant to not hold a value.
Throws:
Any exception thrown by move-constructing any T_i for all i.
Remarks:
The expression inside noexcept is equivalent to the logical AND of is_nothrow_move_constructible_v<T_i> for all i. This function shall not participate in overload resolution unless is_move_constructible_v<T_i> is true for all i.

template <class T> constexpr variant(T&& t);

Requires:
Builds an imaginary function FUN(T_i) for each alternative type T_i. The expression FUN(std::forward<T>(t)) must be valid according to regular overload resolution, otherwise the program is ill-formed. The selected function FUN(T_j) defines the alternative T_j that will be activated by the call to this constructor. [Note:

variant<string, string> v("abc");

is ill-formed, as both alternative types have an equally viable constructor for the argument. — end note]
Effects:
Initializes *this to hold the alternative type T_j as selected by the imaginary function overload resolution described above, and direct-initializes the contained value as if direct-non-list-initializing it with std::forward<T>(t).
Postconditions:
holds_alternative<T_j>(*this) is true, with T_j selected 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> is false. If T_j's selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.

template <class T, class... Args> constexpr explicit variant(in_place_type_t<T>, Args&&... args);

Effects:
Initializes the contained value as if constructing an object of type T with the arguments std::forward<Args>(args)....
Postcondition:
holds_alternative<T>(*this) is true.
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 T in Types... and is_constructible_v<T, Args&&...> is true. If T's selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.

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 T with the arguments il, std::forward<Args>(args)....
Postcondition:
holds_alternative<T>(*this) is true.
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 T in Types... and is_constructible_v<T, initializer_list<U>&, Args&&...> is true. If T's selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.

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_I with the arguments std::forward<Args>(args)....
Postcondition:
index() is I.
Throws:
Any exception thrown by calling the selected constructor of T_I.
Remarks:
This function shall not participate in overload resolution unless I is less than sizeof...(Types) and is_constructible_v<T_I, Args&&...> is true. If T_I's selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.

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_I with the arguments il, std::forward<Args>(args)....
Postcondition:
index() is I.
Remarks:
This function shall not participate in overload resolution unless I is less than sizeof...(Types) and is_constructible_v<T_I, initializer_list<U>&, Args&&...> is true. If T_I's selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.

// 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:
Alloc shall meet the requirements for an Allocator (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() is false, destroys the currently contained value.
Remarks:
If is_trivially_destructible_v<T_i> == true for all T_i then this destructor shall be a trivial destructor.

?.3.3 Assignment [variant.assign]

variant& operator=(const variant& rhs);

Effects:

  • If neither *this nor rhs hold a value, no effect. Otherwise
  • if *this holds a value but rhs does not, destroys the value contained in *this and sets *this to not hold a value. Otherwise,
  • if index() == rhs.index(), assigns the value contained in rhs to the value contained in *this. Otherwise,
  • copies the value contained in rhs to a temporary, then destroys any value contained in *this. Sets *this to hold the same alternative index as rhs and initializes the value contained in *this as if direct-non-list-initializing an object of type T_j with std::forward<T_j>(TMP), with TMP being the temporary and j being rhs.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 of T_j's copy assignment; index() will be j.
  • If an exception is thrown during the call to T_j's copy constructor (with j being rhs.index()), *this will remain unchanged.
  • If an exception is thrown during the call to T_j's move constructor, the variant will 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> is true for all i.

variant& operator=(variant&& rhs) noexcept(see below);

Effects:

  • If neither *this nor rhs hold a value, no effect. Otherwise
  • if *this holds a value but rhs does not, destroys the value contained in *this and sets *this to not hold a value. Otherwise,
  • if index() == rhs.index(), assigns std::forward<T_j>(get<j>(rhs)) to the value contained in *this, with j being index(). Otherwise,
  • destroys any value contained in *this. Sets *this to hold the same alternative index as rhs and initializes the value contained in *this as if direct-non-list-initializing an object of type T_j with std::forward<T_j>(get<j>(rhs)) with j being rhs.index().

If an exception is thrown during the call to T_j's move constructor (with j being rhs.index()), the variant will hold no value. If an exception is thrown during the call to T_j's move assignment, the state of the contained value is as defined by the exception safety guarantee of T_j's move assignment; index() will be j.
Returns:
*this.
Remarks:
This function shall not participate in overload resolution unless is_move_constructible_v<T_i> && is_move_assignable_v<T_i> is true for all i. The expression inside noexcept is equivalent to: is_nothrow_move_constructible_v<T_i> && is_nothrow_move_assignable_v<T_i> for all i.

template <class T> variant& operator=(T&& t) noexcept(see below);

Requires:
Builds an imaginary function FUN(T_i) for each alternative type T_i. The expression FUN(std::forward<T>(t)) must be valid according to regular overload resolution, otherwise the program is ill-formed. The selected function FUN(T_j) defines the alternative T_j that will be activated by the assignment. [Note:

variant<string, string> v;
v = "abc";

is ill-formed, as both alternative types have an equally viable constructor for the argument. — end note]
Effects:
No effect if decay_t<T>(t) is void. If *this holds a T_j, assigns std::forward<T>(t) to the value contained in *this. Otherwise, destroys any value contained in *this, sets *this to hold the alternative type T_j as selected by the imaginary function overload resolution described above, and direct-initializes the contained value as if direct-non-list-initializing it with std::forward<T>(t).
If an exception is thrown during the assignment of std::forward<T>(t) to the value contained in *this, the state of the contained value and t are as defined by the exception safety guarantee of the assignment expression; valueless_by_exception() will be false. If an exception is thrown during the initialization of the contained value, the variant object will not hold a value.
Postcondition:
holds_alternative<T_j>(*this) is true, with T_j selected 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> is false. The expression inside noexcept is equivalent to: is_nothrow_assignable_v<T_i, T&&> && is_nothrow_constructible_v<T_i, T&&> for all i.

?.3.4 Modifiers [variant.mod]

template <class T, class... Args> void emplace(Args&&... args);

Effects:
Destroys the currently contained value if valueless_by_exception() is false. Then direct-initializes the contained value as if constructing a value of type T with the arguments std::forward<Args>(args).... If an exception is thrown during the initialization of the contained value, the variant will not hold a value.
Postcondition:
holds_alternative<T>(*this) is true.
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&&...> is true, and T occurs exactly once in Types....

template <class T, class U, class... Args> void emplace(initializer_list<U> il, Args&&... args);

Effects:
Destroys the currently contained value if valueless_by_exception() is false. Then direct-initializes the contained value as if constructing an object of type T with the arguments il, std::forward<Args>(args).... If an exception is thrown during the initialization of the contained value, the variant will not hold a value.
Postcondition:
holds_alternative<T>(*this) is true.
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&&...> is true, and T occurs exactly once in Types....

template <size_t I, class... Args> void emplace(Args&&... args);

Requires:
I < sizeof...(Types)
Effects:
Destroys the currently contained value if valueless_by_exception() is false. Then direct-initializes the contained value as if constructing a value of type T_I with the arguments std::forward<Args>(args).... If an exception is thrown during the initialization of the contained value, the variant will not hold a value.
Postcondition:
index() is I.
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&&...> is true.

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() is false. Then direct-initializes the contained value as if constructing an object of type T_I with the arguments il, std::forward<Args>(args).... If an exception is thrown during the initialization of the contained value, the variant will not hold a value.
Postcondition:
index() is I.
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&&...> is true.

?.3.5 Value status [variant.status]

constexpr bool valueless_by_exception() const noexcept;

Effects:
Returns whether the variant holds a value (returns false). [Note: A variant will not hold a value if an exception is thrown during a type-changing assignment or emplacement. The latter means that even a variant<float,int> can become valueless_by_exception(), for instance by

struct S { operator int() { throw 42; }};
variant<float, int> v{12.f};
v.emplace<1>(S());
— end note]

constexpr size_t index() const noexcept;

Effects:
If valueless_by_exception() is true, returns tuple_not_found. Otherwise, returns the index of the currently active alternative.

?.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(), calls swap(get<i>(*this), get<i>(rhs)) with i being index(), otherwise
  • exchanges values of rhs and *this.

If an exception is thrown during the call to function swap(get<i>(*this), get<i>(rhs)), the state of the value of this and of rhs is determined by the exception safety guarantee of swap for lvalues of T_i with i being index(). If an exception is thrown during the exchange of the values of *this and rhs, the state of the value of this and of rhs is determined by the exception safety guarantee of variant's move constructor and assignment operator.
Throws:
Any exceptions that is thrown by swap(get<i>(*this), get<i>(rhs)) with i being index() or variant's move constructor and assignment operator.
Remarks:
This function shall not participate in overload resolution unless all alternative types satisfy the Swappable requirements (17.6.3.2) with the corresponding alternative in rhs.

?.4 In-place construction [variant.emplaced]

template <class T> struct in_place_type_t{};
template <class T> constexpr in_place_type_t<T> in_place_type{};
template <size_t I> struct in_place_index_t{};
template <size_t I> constexpr in_place_index_t<I> in_place_index{};

Template specializations of in_place_type_t are empty structure types used as unique types to disambiguate constructor and function overloading. They signal (through the template parameter) the alternative to be constructed. Specifically, variant has a constructor with in_place_type_t<T> as the first argument followed by an argument pack; this indicates that T should be constructed in-place (as if by a call to a placement new expression) with the forwarded argument pack as parameters. If a variant's Types has multiple occurrences of T, in_place_index_t must be used.

Template specializations of in_place_index_t are empty structure types used as unique types to disambiguate constructor and function overloading, and signaling (through the template parameter) the alternative to be constructed. Specifically, variant has a constructor with in_place_index_t<I> as the first argument followed by an argument pack; this indicates that T_I should 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 T occurs exactly once in Types.... Otherwise, the program is ill-formed.
Returns:
the zero-based index of T in Types....

template <size_t I, class... Types>
  constexpr tuple_element_t<I, variant<Types...>>& get(variant<Types...>& v);
template <size_t I, class... Types>
  constexpr tuple_element_t<I, variant<Types...>>&& get(variant<Types...>&& v);// Note A
template <size_t I, class... Types>
  constexpr tuple_element_t<I, variant<Types...>> const& get(const variant<Types...>& v); // Note B
template <size_t I, class... Types>
  constexpr tuple_element_t<I, variant<Types...>> const&& get(const variant<Types...>&& v); // Notes A and B

Requires:
I < sizeof...(Types), and T_I is not a possibly cv-qualified void. Otherwise the program is ill-formed.
Effects:
If v.index() is I, returns a reference to the object stored in the variant. Otherwise, throws an exception of type bad_variant_access.
[Note A:
if T_I is some reference type X&, the return type is X&, not X&&. However, if the element type is a non-reference type T, the return type is T&&. — end note]
[Note B:
Constness is shallow. If T_I is some reference type X&, the return type is X&, not const X&. However, if the element type is non-reference type T, the return type is const 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 T occurs exactly once in Types..., and T is not a possibly cv-qualified void. Otherwise, the program is ill-formed.
Effects:
If v holds a value of type T, returns a reference to that value. Otherwise, throws an exception of type bad_variant_access.

template <size_t I, class... Types>
  constexpr add_pointer_t<tuple_element_t<I, variant<Types...>>> get_if(variant<Types...>* v) noexcept;
template <size_t I, class... Types>
  constexpr add_pointer_t<const tuple_element_t<I, variant<Types...>>> get_if(const variant<Types...>* v) noexcept;

Requires:
I < sizeof...(Types) and T_I is not (possibly cv-qualified) void; otherwise the program is ill-formed.
Returns:
A pointer to the value stored in the variant, if v != nullptr and v->index() == I. Otherwise, returns nullptr.

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 T occurs exactly once in Types..., and T is not a possibly cv-qualified void. Otherwise, the program is ill-formed.
Effects:
Equivalent to return get_if<T_i&>(v) with i being the zero-based index of T in Types....

?.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 to bool, for all i.
Effects:
Equivalent to return (v.valueless_by_exception() && w.valueless_by_exception()) || (v.index() == w.index() && get<i>(v) == get<i>(w)) with i being v.index(), otherwise false.

template <class... Types> constexpr bool operator!=(const variant<Types...>& v, const variant<Types...>& w);

Returns:
!(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 to bool.
Effects:
Equivalent to return (v.index() < w.index()) || (v.index() == w.index() && !v.valueless_by_exception() && get<i>(v) < get<i>(w)) with i being v.index(), otherwise false.

template <class... Types> constexpr bool operator>(const variant<Types...>& v, const variant<Types...>& w);

Returns:
w < v.

template <class... Types> constexpr bool operator<=(const variant<Types...>& v, const variant<Types...>& w);

Returns:
!(v > w).

template <class... Types> constexpr bool operator>=(const variant<Types...>& v, const variant<Types...>& w);

Returns:
!(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, for all combinations of alternative types of all variants.
Effects:
Let is... be vars.index().... Returns INVOKE(forward<Visitor>(vis), get<is>(forward<Variants>(vars))...);.
Remarks:
The return type is the common_type of all possible INVOKE expressions of the Effects element.
Throws:
bad_variant_access if any variant in vars is valueless_by_exception().
Complexity:
For sizeof...(Variants) being 1, the invocation of the callable must be implemented in constant time, i.e. it must not depend on sizeof...(Types). For sizeof...(Variants) greater 1, the invocation of the callable has no complexity requirements.

?.8 Class monostate [variant.monostate]


struct monostate{};

The class monostate can serve as a first alternative type for a variant to make the variant type default constructible.

?.9 monostate relational 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; }

Effects:
Compare two monostate objects. monostate object have only a single state; they thus always compare equal.

?.10 Specialized algorithms [variant.specalg]

template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);

Effects:
Calls v.swap(w).
Remarks:
The expression inside noexcept is equivalent to noexcept(v.swap(w)).

?.11 Class bad_variant_access [variant.bad_variant_access]


class bad_variant_access : public exception {
public:
  bad_variant_access() noexcept;
  const char* what() const noexcept override;
};

Objects of type bad_variant_access are thrown to report invalid accesses to the value of a variant object.

bad_variant_access() noexcept;

Effects:
Constructs a bad_variant_access object.
Postconditions:
what() returns an implementation-defined NTBS.

const char* what() const noexcept override;

Returns:
an implementation-defined NTBS.

?.12 tuple interface to class template variant [variant.tuple]

template <class... Types>
struct tuple_size<variant<Types...>>
  : integral_constant<size_t, sizeof...(Types)> { };

tuple_element<I, variant<Types...>>::type

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 template hash (C++14 §20.9.13) for all T in Types. The template specialization hash<variant<Types...>> shall meet the requirements of class template hash.

template <> struct hash<experimental::monostate>;

The template specialization hash<monostate> shall meet the requirements of class template hash.

?.14 Allocator-related traits [variant.traits]

template <class R, class Alloc>
  struct uses_allocator<experimental::variant<R>, Alloc> : true_type { };

Requires:
Alloc shall be an Allocator (17.6.3.5).
[Note:
Specialization of this trait informs other library components that variant can be constructed with an allocator, even though it does not have a nested allocator_type. — end note]

Conclusion

A variant has proven to be a useful tool. This paper proposes the necessary ingredients.

Acknowledgments

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.

References

1. Working Draft, Technical Specification on C++ Extensions for Library Fundamentals. N4335

2. Improving pair and tuple, revision 2. N4064