Files
cppdraft_translate/cppdraft/variant.md
2025-10-25 03:02:53 +03:00

60 KiB
Raw Permalink Blame History

[variant]

22 General utilities library [utilities]

22.6 Variants [variant]

22.6.1 General [variant.general]

1

#

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

#

In [variant],GET denotes a set of exposition-only function templates ([variant.get]).

22.6.2 Header synopsis [variant.syn]

// mostly freestanding#include // see [compare.syn]namespace std {// [variant.variant], class template varianttemplate<class... Types>class variant; // [variant.helper], variant helper classestemplate struct variant_size; // not definedtemplate struct variant_size; templateconstexpr size_t variant_size_v = variant_size::value; template<class... Types>struct variant_size<variant<Types...>>; template<size_t I, class T> struct variant_alternative; // not definedtemplate<size_t I, class T> struct variant_alternative<I, const T>; template<size_t I, class T>using variant_alternative_t = typename variant_alternative<I, T>::type; template<size_t I, class... Types>struct variant_alternative<I, variant<Types...>>; inline constexpr size_t variant_npos = -1; // [variant.get], value accesstemplate<class T, class... Types>constexpr bool holds_alternative(const variant<Types...>&) noexcept; template<size_t I, class... Types>constexpr variant_alternative_t<I, variant<Types...>>& get(variant<Types...>&); // freestanding-deletedtemplate<size_t I, class... Types>constexpr variant_alternative_t<I, variant<Types...>>&& get(variant<Types...>&&); // freestanding-deletedtemplate<size_t I, class... Types>constexpr const variant_alternative_t<I, variant<Types...>>& get(const variant<Types...>&); // freestanding-deletedtemplate<size_t I, class... Types>constexpr const variant_alternative_t<I, variant<Types...>>&& get(const variant<Types...>&&); // freestanding-deletedtemplate<class T, class... Types>constexpr T& get(variant<Types...>&); // freestanding-deletedtemplate<class T, class... Types>constexpr T&& get(variant<Types...>&&); // freestanding-deletedtemplate<class T, class... Types>constexpr const T& get(const variant<Types...>&); // freestanding-deletedtemplate<class T, class... Types>constexpr const T&& get(const variant<Types...>&&); // freestanding-deletedtemplate<size_t I, class... Types>constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>> get_if(variant<Types...>) noexcept; template<size_t I, class... Types>constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>> get_if(const variant<Types...>) noexcept; template<class T, class... Types>constexpr add_pointer_t get_if(variant<Types...>) noexcept; template<class T, class... Types>constexpr add_pointer_t get_if(const variant<Types...>) noexcept; // [variant.relops], relational operatorstemplate<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...>&); template<class... Types> requires (three_way_comparable && ...)constexpr common_comparison_category_t<compare_three_way_result_t...>operator<=>(const variant<Types...>&, const variant<Types...>&); // [variant.visit], visitationtemplate<class Visitor, class... Variants>constexpr see below visit(Visitor&&, Variants&&...); template<class R, class Visitor, class... Variants>constexpr R visit(Visitor&&, Variants&&...); // [variant.monostate], class monostatestruct monostate; // [variant.monostate.relops], monostate relational operatorsconstexpr bool operator==(monostate, monostate) noexcept; constexpr strong_ordering operator<=>(monostate, monostate) noexcept; // [variant.specalg], specialized algorithmstemplate<class... Types>constexpr void swap(variant<Types...>&, variant<Types...>&) noexcept(see below); // [variant.bad.access], class bad_variant_accessclass bad_variant_access; // [variant.hash], hash supporttemplate struct hash; template<class... Types> struct hash<variant<Types...>>; template<> struct hash;}

22.6.3 Class template variant [variant.variant]

22.6.3.1 General [variant.variant.general]

namespace std {template<class... Types>class variant {public:// [variant.ctor], constructorsconstexpr variant() noexcept(see below); constexpr variant(const variant&); constexpr variant(variant&&) noexcept(see below); templateconstexpr variant(T&&) noexcept(see below); template<class T, class... Args>constexpr explicit variant(in_place_type_t, Args&&...); template<class T, class U, class... Args>constexpr explicit variant(in_place_type_t, initializer_list, Args&&...); template<size_t I, class... Args>constexpr explicit variant(in_place_index_t, Args&&...); template<size_t I, class U, class... Args>constexpr explicit variant(in_place_index_t, initializer_list, Args&&...); // [variant.dtor], destructorconstexpr ~variant(); // [variant.assign], assignmentconstexpr variant& operator=(const variant&); constexpr variant& operator=(variant&&) noexcept(see below); template constexpr variant& operator=(T&&) noexcept(see below); // [variant.mod], modifierstemplate<class T, class... Args>constexpr T& emplace(Args&&...); template<class T, class U, class... Args>constexpr T& emplace(initializer_list, Args&&...); template<size_t I, class... Args>constexpr variant_alternative_t<I, variant<Types...>>& emplace(Args&&...); template<size_t I, class U, class... Args>constexpr variant_alternative_t<I, variant<Types...>>& emplace(initializer_list, Args&&...); // [variant.status], value statusconstexpr bool valueless_by_exception() const noexcept; constexpr size_t index() const noexcept; // [variant.swap], swapconstexpr void swap(variant&) noexcept(see below); // [variant.visit], visitationtemplate<class Self, class Visitor>constexpr decltype(auto) visit(this Self&&, Visitor&&); template<class R, class Self, class Visitor>constexpr R visit(this Self&&, Visitor&&); };}

1

#

Any instance of variant at any given time either holds a value of one of its alternative types or 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 nested within ([intro.object]) thevariant object.

2

#

All types in Types shall meet the Cpp17Destructible requirements (Table 35).

3

#

A program that instantiates the definition of variant with no template arguments is ill-formed.

4

#

If a program declares an explicit or partial specialization of variant, the program is ill-formed, no diagnostic required.

22.6.3.2 Constructors [variant.ctor]

1

#

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

🔗

constexpr variant() noexcept(see below);

2

#

Constraints: is_default_constructible_v is true.

3

#

Effects: Constructs a variant holding a value-initialized value of type T0.

4

#

Postconditions: valueless_by_exception() is false and index() is 0.

5

#

Throws: Any exception thrown by the value-initialization of T0.

6

#

Remarks: This function is constexpr if and only if the value-initialization of the alternative type T0 would be constexpr-suitable ([dcl.constexpr]).

The exception specification is equivalent tois_nothrow_default_constructible_v.

[Note 1:

See also class monostate.

— end note]

🔗

constexpr variant(const variant& w);

7

#

Effects: If w holds a value, initializes the variant to hold the same alternative as w and direct-initializes the contained value with GET(w), where j is w.index().

Otherwise, initializes the variant to not hold a value.

8

#

Throws: Any exception thrown by direct-initializing any Ti for all i.

9

#

Remarks: This constructor is defined as deleted unlessis_copy_constructible_v is true for all i.

If is_trivially_copy_constructible_v is true for all i, this constructor is trivial.

🔗

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

10

#

Constraints: is_move_constructible_v is true for all i.

11

#

Effects: If w holds a value, initializes the variant to hold the same alternative as w and direct-initializes the contained value withGET(std::move(w)), where j is w.index().

Otherwise, initializes the variant to not hold a value.

12

#

Throws: Any exception thrown by move-constructing any Ti for all i.

13

#

Remarks: The exception specification is equivalent to the logical and ofis_nothrow_move_constructible_v for all i.

If is_trivially_move_constructible_v is true for all i, this constructor is trivial.

🔗

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

14

#

Let Tj be a type that is determined as follows: build an imaginary function FUN(Ti) for each alternative type Ti for which Ti x[] = {std::forward(t)}; is well-formed for some invented variable x.

The overload FUN(Tj) selected by overload resolution for the expression FUN(std::forward(t)) defines the alternative Tj which is the type of the contained value after construction.

15

#

Constraints:

sizeof...(Types) is nonzero,

is_same_v<remove_cvref_t, variant> is false,

remove_cvref_t is neither a specialization of in_place_type_t nor a specialization of in_place_index_t,

is_constructible_v<Tj, T> is true, and

the expression FUN(std::forward(t)) (with FUN being the above-mentioned set of imaginary functions) is well-formed. [Note 2: variant<string, string> v("abc"); is ill-formed, as both alternative types have an equally viable constructor for the argument. — end note]

16

#

Effects: Initializes *this to hold the alternative type Tj and direct-non-list-initializes the contained value with std::forward(t).

17

#

Postconditions: holds_alternative(*this) is true.

18

#

Throws: Any exception thrown by the initialization of the selected alternative Tj.

19

#

Remarks: The exception specification is equivalent tois_nothrow_constructible_v<Tj, T>.

If Tj's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

🔗

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

20

#

Constraints:

There is exactly one occurrence of T in Types... and

is_constructible_v<T, Args...> is true.

21

#

Effects: Direct-non-list-initializes the contained value of type T with std::forward(args)....

22

#

Postconditions: holds_alternative(*this) is true.

23

#

Throws: Any exception thrown by calling the selected constructor of T.

24

#

Remarks: If T's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

🔗

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

25

#

Constraints:

There is exactly one occurrence of T in Types... and

is_constructible_v<T, initializer_list&, Args...> is true.

26

#

Effects: Direct-non-list-initializes the contained value of type T with il, std::forward(args)....

27

#

Postconditions: holds_alternative(*this) is true.

28

#

Throws: Any exception thrown by calling the selected constructor of T.

29

#

Remarks: If T's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

🔗

template<size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&... args);

30

#

Constraints:

I is less than sizeof...(Types) and

is_constructible_v<TI, Args...> is true.

31

#

Effects: Direct-non-list-initializes the contained value of type TI with std::forward(args)....

32

#

Postconditions: index() is I.

33

#

Throws: Any exception thrown by calling the selected constructor of TI.

34

#

Remarks: If TI's selected constructor is a constexpr constructor, this constructor is 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);

35

#

Constraints:

I is less than sizeof...(Types) and

is_constructible_v<TI, initializer_list&, Args...> is true.

36

#

Effects: Direct-non-list-initializes the contained value of type TI with il, std::forward(args)....

37

#

Postconditions: index() is I.

38

#

Remarks: If TI's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

22.6.3.3 Destructor [variant.dtor]

🔗

constexpr ~variant();

1

#

Effects: If valueless_by_exception() is false, destroys the currently contained value.

2

#

Remarks: If is_trivially_destructible_v is true for all Ti, then this destructor is trivial.

22.6.3.4 Assignment [variant.assign]

🔗

constexpr variant& operator=(const variant& rhs);

1

#

Let j be rhs.index().

2

#

Effects:

  • (2.1)

    If neither *this nor rhs holds a value, there is no effect.

  • (2.2)

    Otherwise, if *this holds a value but rhs does not, destroys the value contained in *this and sets *this to not hold a value.

  • (2.3)

    Otherwise, if index() == j, assigns the value contained in rhs to the value contained in *this.

  • (2.4)

    Otherwise, if either is_nothrow_copy_constructible_v is true oris_nothrow_move_constructible_v is false, equivalent to emplace(GET(rhs)).

  • (2.5)

    Otherwise, equivalent to operator=(variant(rhs)).

3

#

Postconditions: index() == rhs.index().

4

#

Returns: *this.

5

#

Remarks: This operator is defined as deleted unlessis_copy_constructible_v &&is_copy_assignable_v is true for all i.

If is_trivially_copy_constructible_v &&is_trivially_copy_assignable_v &&is_trivially_destructible_v is true for all i, this assignment operator is trivial.

🔗

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

6

#

Let j be rhs.index().

7

#

Constraints: is_move_constructible_v &&is_move_assignable_v istrue for all i.

8

#

Effects:

  • (8.1)

    If neither *this nor rhs holds a value, there is no effect.

  • (8.2)

    Otherwise, if *this holds a value but rhs does not, destroys the value contained in *this and sets *this to not hold a value.

  • (8.3)

    Otherwise, if index() == j, assigns GET(std::move(rhs)) to the value contained in *this.

  • (8.4)

    Otherwise, equivalent to emplace(GET(std::move(rhs))).

9

#

Returns: *this.

10

#

Remarks: If is_trivially_move_constructible_v &&is_trivially_move_assignable_v &&is_trivially_destructible_v is true for all i, this assignment operator is trivial.

The exception specification is equivalent tois_nothrow_move_constructible_v && is_nothrow_move_assignable_v for all i.

  • (10.1)

    If an exception is thrown during the call to Tj's move construction (with j being rhs.index()), the variant will hold no value.

  • (10.2)

    If an exception is thrown during the call to Tj's move assignment, the state of the contained value is as defined by the exception safety guarantee of Tj's move assignment; index() will be j.

🔗

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

11

#

Let Tj be a type that is determined as follows: build an imaginary function FUN(Ti) for each alternative type Ti for which Ti x[] = {std::forward(t)}; is well-formed for some invented variable x.

The overload FUN(Tj) selected by overload resolution for the expression FUN(std::forward(t)) defines the alternative Tj which is the type of the contained value after assignment.

12

#

Constraints:

is_same_v<remove_cvref_t, variant> is false,

is_assignable_v<Tj&, T> && is_constructible_v<Tj, T> is true, and

the expression FUN(std::forward(t)) (with FUN being the above-mentioned set of imaginary functions) is well-formed. [Note 1: variant<string, string> v; v = "abc"; is ill-formed, as both alternative types have an equally viable constructor for the argument. — end note]

13

#

Effects:

  • (13.1)

    If *this holds a Tj, assigns std::forward(t) to the value contained in *this.

  • (13.2)

    Otherwise, if is_nothrow_constructible_v<Tj, T> ||!is_nothrow_move_constructible_v is true, equivalent to emplace(std::forward(t)).

  • (13.3)

    Otherwise, equivalent to emplace(Tj(std::forward(t))).

14

#

Postconditions: holds_alternative(*this) is true, with Tj selected by the imaginary function overload resolution described above.

15

#

Returns: *this.

16

#

Remarks: The exception specification is equivalent to:is_nothrow_assignable_v<Tj&, T> && is_nothrow_constructible_v<Tj, T>

  • (16.1)

    If an exception is thrown during the assignment of std::forward(t) to the value contained in *this, the state of the contained value andt are as defined by the exception safety guarantee of the assignment expression; valueless_by_exception() will be false.

  • (16.2)

    If an exception is thrown during the initialization of the contained value, the variant object is permitted to not hold a value.

22.6.3.5 Modifiers [variant.mod]

🔗

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

1

#

Constraints: is_constructible_v<T, Args...> is true, andT occurs exactly once in Types.

2

#

Effects: Equivalent to:return emplace(std::forward(args)...); where I is the zero-based index of T in Types.

🔗

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

3

#

Constraints: is_constructible_v<T, initializer_list&, Args...> is true, and T occurs exactly once in Types.

4

#

Effects: Equivalent to:return emplace(il, std::forward(args)...); where I is the zero-based index of T in Types.

🔗

template<size_t I, class... Args> constexpr variant_alternative_t<I, variant<Types...>>& emplace(Args&&... args);

5

#

Mandates: I < sizeof...(Types).

6

#

Constraints: is_constructible_v<TI, Args...> is true.

7

#

Effects: Destroys the currently contained value if valueless_by_exception() is false.

Then direct-non-list-initializes the contained value of type TI with the arguments std::forward(args)....

8

#

Postconditions: index() is I.

9

#

Returns: A reference to the new contained value.

10

#

Throws: Any exception thrown during the initialization of the contained value.

11

#

Remarks: If an exception is thrown during the initialization of the contained value, the variant is permitted to not hold a value.

🔗

template<size_t I, class U, class... Args> constexpr variant_alternative_t<I, variant<Types...>>& emplace(initializer_list<U> il, Args&&... args);

12

#

Mandates: I < sizeof...(Types).

13

#

Constraints: is_constructible_v<TI, initializer_list&, Args...> is true.

14

#

Effects: Destroys the currently contained value if valueless_by_exception() is false.

Then direct-non-list-initializes the contained value of type TI with il, std::forward(args)....

15

#

Postconditions: index() is I.

16

#

Returns: A reference to the new contained value.

17

#

Throws: Any exception thrown during the initialization of the contained value.

18

#

Remarks: If an exception is thrown during the initialization of the contained value, the variant is permitted to not hold a value.

22.6.3.6 Value status [variant.status]

🔗

constexpr bool valueless_by_exception() const noexcept;

1

#

Effects: Returns false if and only if the variant holds a value.

2

#

[Note 1:

It is possible for a variant to hold no value if an exception is thrown during a type-changing assignment or emplacement.

The latter means that even avariant<float, int> can become valueless_by_exception(), for instance bystruct S { operator int() { throw 42; }}; variant<float, int> v{12.f}; v.emplace<1>(S());

— end note]

🔗

constexpr size_t index() const noexcept;

3

#

Effects: If valueless_by_exception() is true, returns variant_npos.

Otherwise, returns the zero-based index of the alternative of the contained value.

22.6.3.7 Swap [variant.swap]

🔗

constexpr void swap(variant& rhs) noexcept(see below);

1

#

Mandates: is_move_constructible_v is true for all i.

2

#

Preconditions: Each Ti meets the Cpp17Swappable requirements ([swappable.requirements]).

3

#

Effects:

  • (3.1)

    If valueless_by_exception() && rhs.valueless_by_exception() no effect.

  • (3.2)

    Otherwise, if index() == rhs.index(), calls swap(GET(*this), GET(rhs)) where i is index().

  • (3.3)

    Otherwise, exchanges values of rhs and *this.

4

#

Throws: If index() == rhs.index(), any exception thrown by swap(GET(*this), GET(rhs)) with i being index().

Otherwise, any exception thrown by the move constructor of Ti or Tj with i being index() and j being rhs.index().

5

#

Remarks: If an exception is thrown during the call to function swap(GET(*this), GET(rhs)), the states of the contained values of *this and of rhs are determined by the exception safety guarantee of swap for lvalues ofTi with i being index().

If an exception is thrown during the exchange of the values of *this and rhs, the states of the values of *this and of rhs are determined by the exception safety guarantee of variant's move constructor.

The exception specification is equivalent to the logical and ofis_nothrow_move_constructible_v && is_nothrow_swappable_v for all i.

22.6.4 variant helper classes [variant.helper]

🔗

template<class T> struct variant_size;

1

#

All specializations of variant_size meet theCpp17UnaryTypeTrait requirements ([meta.rqmts]) with a base characteristic of integral_constant<size_t, N> for some N.

🔗

template<class T> struct variant_size<const T>;

2

#

Let VS denote variant_size of the cv-unqualified type T.

Then each specialization of the template meets theCpp17UnaryTypeTrait requirements ([meta.rqmts]) with a base characteristic of integral_constant<size_t, VS::value>.

🔗

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

🔗

template<size_t I, class T> struct variant_alternative<I, const T>;

3

#

Let VA denote variant_alternative<I, T> of the cv-unqualified type T.

Then each specialization of the template meets the Cpp17TransformationTrait requirements ([meta.rqmts]) with a member typedef type that names the type add_const_t<VA::type>.

🔗

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

4

#

Mandates: I < sizeof...(Types).

5

#

Type: The type TI.

22.6.5 Value access [variant.get]

🔗

template<class T, class... Types> constexpr bool holds_alternative(const variant<Types...>& v) noexcept;

1

#

Mandates: The type T occurs exactly once in Types.

2

#

Returns: true if index() is equal to the zero-based index of T in Types.

🔗

template<size_t I, class... Types> constexpr variant_alternative_t<I, variant<Types...>>& GET(variant<Types...>& v); // exposition only template<size_t I, class... Types> constexpr variant_alternative_t<I, variant<Types...>>&& GET(variant<Types...>&& v); // exposition only template<size_t I, class... Types> constexpr const variant_alternative_t<I, variant<Types...>>& GET(const variant<Types...>& v); // exposition only template<size_t I, class... Types> constexpr const variant_alternative_t<I, variant<Types...>>&& GET(const variant<Types...>&& v); // exposition only

3

#

Mandates: I < sizeof...(Types).

4

#

Preconditions: v.index() is I.

5

#

Returns: A reference to the object stored in the variant.

🔗

template<size_t I, class... Types> constexpr variant_alternative_t<I, variant<Types...>>& get(variant<Types...>& v); template<size_t I, class... Types> constexpr variant_alternative_t<I, variant<Types...>>&& get(variant<Types...>&& v); template<size_t I, class... Types> constexpr const variant_alternative_t<I, variant<Types...>>& get(const variant<Types...>& v); template<size_t I, class... Types> constexpr const variant_alternative_t<I, variant<Types...>>&& get(const variant<Types...>&& v);

6

#

Mandates: I < sizeof...(Types).

7

#

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.

🔗

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);

8

#

Mandates: The type T occurs exactly once in Types.

9

#

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<variant_alternative_t<I, variant<Types...>>> get_if(variant<Types...>* v) noexcept; template<size_t I, class... Types> constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>> get_if(const variant<Types...>* v) noexcept;

10

#

Mandates: I < sizeof...(Types).

11

#

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;

12

#

Mandates: The type T occurs exactly once in Types.

13

#

Effects: Equivalent to: return get_if(v); with i being the zero-based index of T in Types.

22.6.6 Relational operators [variant.relops]

🔗

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

1

#

Constraints: GET(v) == GET(w) is a valid expression that is convertible to bool, for all i.

2

#

Returns: If v.index() != w.index(), false; otherwise if v.valueless_by_exception(), true; otherwise GET(v) == GET(w) with i being v.index().

🔗

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

3

#

Constraints: GET(v) != GET(w) is a valid expression that is convertible to bool, for all i.

4

#

Returns: If v.index() != w.index(), true; otherwise if v.valueless_by_exception(), false; otherwise GET(v) != GET(w) with i being v.index().

🔗

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

5

#

Constraints: GET(v) < GET(w) is a valid expression that is convertible to bool, for all i.

6

#

Returns: If w.valueless_by_exception(), false; otherwise if v.valueless_by_exception(), true; otherwise, if v.index() < w.index(), true; otherwise if v.index() > w.index(), false; otherwise GET(v) < GET(w) with i being v.index().

🔗

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

7

#

Constraints: GET(v) > GET(w) is a valid expression that is convertible to bool, for all i.

8

#

Returns: If v.valueless_by_exception(), false; otherwise if w.valueless_by_exception(), true; otherwise, if v.index() > w.index(), true; otherwise if v.index() < w.index(), false; otherwise GET(v) > GET(w) with i being v.index().

🔗

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

9

#

Constraints: GET(v) <= GET(w) is a valid expression that is convertible to bool, for all i.

10

#

Returns: If v.valueless_by_exception(), true; otherwise if w.valueless_by_exception(), false; otherwise, if v.index() < w.index(), true; otherwise if v.index() > w.index(), false; otherwise GET(v) <= GET(w) with i being v.index().

🔗

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

11

#

Constraints: GET(v) >= GET(w) is a valid expression that is convertible to bool, for all i.

12

#

Returns: If w.valueless_by_exception(), true; otherwise if v.valueless_by_exception(), false; otherwise, if v.index() > w.index(), true; otherwise if v.index() < w.index(), false; otherwise GET(v) >= GET(w) with i being v.index().

🔗

template<class... Types> requires ([three_way_comparable](cmp.concept#concept:three_way_comparable "17.12.4Concept three_­way_­comparable[cmp.concept]")<Types> && ...) constexpr common_comparison_category_t<compare_three_way_result_t<Types>...> operator<=>(const variant<Types...>& v, const variant<Types...>& w);

13

#

Effects: Equivalent to:if (v.valueless_by_exception() && w.valueless_by_exception())return strong_ordering::equal;if (v.valueless_by_exception()) return strong_ordering::less;if (w.valueless_by_exception()) return strong_ordering::greater;if (auto c = v.index() <=> w.index(); c != 0) return c;return GET(v) <=> GET(w); with i being v.index().

22.6.7 Visitation [variant.visit]

🔗

template<class Visitor, class... Variants> constexpr see below visit(Visitor&& vis, Variants&&... vars); template<class R, class Visitor, class... Variants> constexpr R visit(Visitor&& vis, Variants&&... vars);

1

#

Let as-variant denote the following exposition-only function templates:template<class... Ts>constexpr auto&& as-variant(variant<Ts...>& var) { return var; }template<class... Ts>constexpr auto&& as-variant(const variant<Ts...>& var) { return var; }template<class... Ts>constexpr auto&& as-variant(variant<Ts...>&& var) { return std::move(var); }template<class... Ts>constexpr auto&& as-variant(const variant<Ts...>&& var) { return std::move(var); }

Let n be sizeof...(Variants).

For each 0≤i<n, letVi denote the type
decltype(as-variant(std::forward(varsi))).

2

#

Constraints: Vi is a valid type for all 0≤i<n.

3

#

Let V denote the pack of types Vi.

4

#

Let m be a pack of n values of type size_t.

Such a pack is valid if
‰¤mi<variant_size_v<remove_reference_t> for all 0≤i<n.

For each valid pack m, let e(m) denote the expression:INVOKE(std::forward(vis), GET(std::forward(vars))...) // see [func.require] for the first form andINVOKE(std::forward(vis), GET(std::forward(vars))...) // see [func.require] for the second form.

5

#

Mandates: For each valid pack m, e(m) is a valid expression.

All such expressions are of the same type and value category.

6

#

Returns: e(m), where m is the pack for whichmi is as-variant(varsi).index() for all 0≤i<n.

The return type is decltype(e(m)) for the first form.

7

#

Throws: bad_variant_access if(as-variant(vars).valueless_by_exception() || ...) is true.

8

#

Complexity: For n ≤ 1, the invocation of the callable object is implemented in constant time, i.e., for n=1, it does not depend on the number of alternative types of V0.

For n>1, the invocation of the callable object has no complexity requirements.

🔗

template<class Self, class Visitor> constexpr decltype(auto) visit(this Self&& self, Visitor&& vis);

9

#

Let V beOVERRIDE_REF(Self&&, COPY_CONST(remove_reference_t, variant)) ([forward]).

10

#

Constraints: The call to visit does not use an explicit template-argument-list that begins with a type template-argument.

11

#

Effects: Equivalent to: return std::visit(std::forward(vis), (V)self);

🔗

template<class R, class Self, class Visitor> constexpr R visit(this Self&& self, Visitor&& vis);

12

#

Let V beOVERRIDE_REF(Self&&, COPY_CONST(remove_reference_t, variant)) ([forward]).

13

#

Effects: Equivalent to: return std::visit(std::forward(vis), (V)self);

22.6.8 Class monostate [variant.monostate]

🔗

struct monostate{};

1

#

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

22.6.9 monostate relational operators [variant.monostate.relops]

🔗

constexpr bool operator==(monostate, monostate) noexcept { return true; } constexpr strong_ordering operator<=>(monostate, monostate) noexcept { return strong_ordering::equal; }

1

#

[Note 1:

monostate objects have only a single state; they thus always compare equal.

— end note]

22.6.10 Specialized algorithms [variant.specalg]

🔗

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

1

#

Constraints: is_move_constructible_v && is_swappable_v is true for all i.

2

#

Effects: Equivalent to v.swap(w).

3

#

Remarks: The exception specification is equivalent to noexcept(v.swap(w)).

22.6.11 Class bad_variant_access [variant.bad.access]

namespace std {class bad_variant_access : public exception {public:// see [exception] for the specification of the special member functionsconstexpr const char* what() const noexcept override; };}

1

#

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

🔗

constexpr const char* what() const noexcept override;

2

#

Returns: An implementation-defined ntbs, which during constant evaluation is encoded with the ordinary literal encoding ([lex.ccon]).

22.6.12 Hash support [variant.hash]

🔗

template<class... Types> struct hash<variant<Types...>>;

1

#

The specialization hash<variant<Types...>> is enabled ([unord.hash]) if and only if every specialization in hash<remove_const_t>... is enabled.

The member functions are not guaranteed to be noexcept.

🔗

template<> struct hash<monostate>;

2

#

The specialization is enabled ([unord.hash]).