23 General utilities library [utilities]

23.7 Variants [variant]

23.7.3 Class template variant [variant.variant]

namespace std {
  template <class... Types>
    class variant {
    public:
      // [variant.ctor], constructors
      constexpr variant() noexcept(see below);
      variant(const variant&);
      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&, const variant&);
      template <class Alloc>
        variant(allocator_arg_t, const Alloc&, 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&&...);

      // [variant.dtor], destructor
      ~variant();

      // [variant.assign], assignment
      variant& operator=(const variant&);
      variant& operator=(variant&&) noexcept(see below);

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

      // [variant.mod], modifiers
      template <class T, class... Args>
        T& emplace(Args&&...);
      template <class T, class U, class... Args>
        T& emplace(initializer_list<U>, Args&&...);
      template <size_t I, class... Args>
        variant_alternative_t<I, variant<Types...>>& emplace(Args&&...);
      template <size_t I, class U, class... Args>
        variant_alternative_t<I, variant<Types...>>& emplace(initializer_list<U>, Args&&...);

      // [variant.status], value status
      constexpr bool valueless_by_exception() const noexcept;
      constexpr size_t index() const noexcept;

      // [variant.swap], swap
      void swap(variant&) noexcept(see below);
    };
}

Любой экземпляр variant в любой момент времени либо содержит значение одного из его альтернативных типов, либо не имеет значения. Когда экземпляр variant содержит значение альтернативного типа T, это означает, что значение типа T, называемое variant объектом contained value, выделяется в хранилище variant объекта. Реализациям не разрешается использовать дополнительное хранилище, такое как динамическая память, для выделения содержащегося значения. Содержимое значение должно быть размещено в области variant хранилища, подходящей для всех типов в Types.... Это определяется реализацией, поддерживаются ли более выровненных типов.

Все типы Types... должны быть (возможно, квалифицированными cv) типами объектов, которые не являются массивами.

Программа, которая реализует определение variant без аргументов шаблона, плохо сформирована.

23.7.3.1 Constructors [variant.ctor]

В описаниях , которые следуют, давайте i быть в диапазоне [0, sizeof...(Types)), и Ti быть ith типа в Types....

constexpr variant() noexcept(see below);

Effects: Создает variant удерживающее значение типа, инициализированное значением T0.

Postconditions: valueless_­by_­exception() есть false и index() есть 0.

Throws: Любое исключение, вызванное инициализацией значения T0.

Remarks: Эта функция должна быть в constexpr том и только в том случае, если инициализация значения альтернативного типа T0 удовлетворяет требованиям для функции constexpr. Выражение внутри noexcept эквивалентно is_­nothrow_­default_­constructible_­v<T0>. Эта функция не будет участвовать в разрешении перегрузки , если is_­default_­constructible_­v<T0> не true. [ Note: См. Также класс monostate. ] end note

variant(const variant& w);

Effects: Если w содержит значение, инициализирует variant для хранения той же альтернативы, что w и, и напрямую инициализирует содержащееся значение с помощью get<j>(w), где j is w.index(). В противном случае инициализирует, variant чтобы не хранить значение.

Throws: Любое исключение, вызванное прямой инициализацией any Ti for all i.

Remarks: Эта функция не должна участвовать в разрешении перегрузки, за исключением случаев, когда она is_­copy_­constructible_­v<Ti> предназначена true для всех i.

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

Effects: Если w содержит значение, инициализирует variant для хранения той же альтернативы, что w и, и напрямую инициализирует содержащееся значение с помощью get<j>(std​::​move(w)), где j is w.index(). В противном случае инициализирует, variant чтобы не хранить значение.

Throws: Любое исключение, создаваемое перемещением любого Ti для всех i.

Remarks: Выражение внутри noexcept эквивалентно логическому И is_­nothrow_­move_­constructible_­v<Ti> для всех i. Эта функция не должна участвовать в разрешении перегрузки, за исключением случаев, когда она is_­move_­constructible_­v<Ti> предназначена true для всех i.

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

Позвольте Tj быть типом, который определяется следующим образом: построить мнимую функцию FUN(Ti) для каждого альтернативного типа Ti. Перегрузка, FUN(Tj) выбранная разрешением перегрузки для выражения, FUN(std​::​forward<T>(​t)) определяет альтернативу, Tj которая является типом содержащегося значения после построения.

Effects: Инициализируется *this для хранения альтернативного типа Tj и напрямую инициализирует содержащееся в нем значение, как если бы оно было инициализировано напрямую без использования списка std​::​forward<T>(t).

Postconditions: holds_­alternative<Tj>(*this) есть true.

Throws: Любое исключение, вызванное инициализацией выбранной альтернативы Tj.

Remarks: Эта функция не будет участвовать в разрешении перегрузки , если только is_­same_­v<decay_­t<T>, variant> не будет false, если только decay_­t<T> не является ни специализации , in_­place_­type_­t ни специализации in_­place_­index_­t, если только is_­constructible_­v<Tj, T> не будет true, и если выражение (с того вышеупомянутый набор функций мнимых) хорошо сформированы.FUN(std​::​forward<T>(t)) FUN

[Note:

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

неправильно сформирован, поскольку оба альтернативных типа имеют одинаково жизнеспособный конструктор для аргумента. ] end note

Выражение внутри noexcept эквивалентно is_­nothrow_­constructible_­v<Tj, T>. Если Tjвыбранный конструктор является конструктором constexpr, этот конструктор должен быть конструктором constexpr.

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

Effects: Инициализирует содержащееся значение, как если бы инициализирует объект типа напрямую без использования списка T с аргументами std​::​forward<Args>(args)....

Postconditions: holds_­alternative<T>(*this) есть true.

Throws: Любое исключение, вызванное вызовом выбранного конструктора T.

Remarks: Эта функция не должна участвовать в разрешении перегрузки, если только одно вхождение T in Types... и is_­constructible_­v<T, Args...> is true. Если Tвыбранный конструктор является конструктором constexpr, этот конструктор должен быть конструктором constexpr.

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

Effects: Инициализирует содержащееся значение, как если бы инициализирует объект типа напрямую без использования списка T с аргументами il, std​::​forward<Args>(args)....

Postconditions: holds_­alternative<T>(*this) есть true.

Throws: Любое исключение, вызванное вызовом выбранного конструктора T.

Remarks: Эта функция не должна участвовать в разрешении перегрузки, если только одно вхождение T in Types... и is_­constructible_­v<T, initializer_­list<U>&, Args...> is true. Если Tвыбранный конструктор является конструктором constexpr, этот конструктор должен быть конструктором constexpr.

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

Effects: Инициализирует содержащееся значение, как если бы инициализирует объект типа напрямую без использования списка TI с аргументами std​::​forward<Args>(args)....

Postconditions: index() есть I.

Throws: Любое исключение, вызванное вызовом выбранного конструктора TI.

Remarks: Эта функция не должна участвовать в разрешении перегрузки, если только

  • I меньше чем sizeof...(Types) и

  • is_­constructible_­v<TI, Args...> есть true.

Если TIвыбранный конструктор является конструктором constexpr, этот конструктор должен быть конструктором constexpr.

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

Effects: Инициализирует содержащееся значение, как если бы инициализирует объект типа напрямую без использования списка TI с аргументами il, std​::​forward<Args>(args)....

Postconditions: index() есть I.

Remarks: Эта функция не должна участвовать в разрешении перегрузки, если только

  • I меньше чем sizeof...(Types) и

  • is_­constructible_­v<TI, initializer_­list<U>&, Args...> есть true.

Если TIвыбранный конструктор является конструктором constexpr, этот конструктор должен быть конструктором constexpr.

// 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 должны соответствовать требованиям для Allocator.

Effects: Эквивалентен предыдущим конструкторам, за исключением того, что содержащееся значение создается с помощью uses-allocator construction.

23.7.3.2 Destructor [variant.dtor]

~variant();

Effects: Если valueless_­by_­exception() есть false, уничтожает текущее содержащееся значение.

Remarks: Если is_­trivially_­destructible_­v<Ti> == true для всех, Ti то этот деструктор должен быть тривиальным деструктором.

23.7.3.3 Assignment [variant.assign]

variant& operator=(const variant& rhs);

Пусть j будет rhs.index().

Effects:

  • Если ни один из значений *this не rhs имеет значения, эффекта нет. Иначе,

  • если *this содержит значение, но rhs не содержит, уничтожает значение, содержащееся в, *this и устанавливает *this значение, не содержащее значения. Иначе,

  • if index() == jприсваивает значение, содержащееся в, rhs значению, содержащемуся в *this. Иначе,

  • если либо, is_­nothrow_­copy_­constructible_­v<Tj> либо !is_­nothrow_­move_­constructible_­v<Tj> равно true, эквивалентно emplace<j>(get<j>(rhs)). Иначе,

  • эквивалентно operator=(variant(rhs)).

Returns: *this.

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

Remarks: Эта функция не должна участвовать в разрешении перегрузки, за исключением случаев, когда она is_­copy_­constructible_­v<Ti> && is_­copy_­assignable_­v<Ti> предназначена true для всех i.

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

Пусть j будет rhs.index().

Effects:

  • Если ни один из значений *this не rhs имеет значения, эффекта нет. Иначе,

  • если *this содержит значение, но rhs не содержит, уничтожает значение, содержащееся в, *this и устанавливает *this значение, не содержащее значения. Иначе,

  • if index() == jприсваивает get<j>(std​::​move(rhs)) значение, содержащееся в *this. Иначе,

  • эквивалентно emplace<j>(get<j>(std​::​move(rhs))).

Returns: *this.

Remarks: Эта функция не должна участвовать в разрешении перегрузки, за исключением случаев, когда она is_­move_­constructible_­v<Ti> && is_­move_­assignable_­v<Ti> предназначена true для всех i. Выражение внутри noexcept эквивалентно: is_­nothrow_­move_­constructible_­v<Ti> && is_­nothrow_­move_­assignable_­v<Ti> для всех i.

  • Если во время вызова Tjконструкции move вызывается исключение (при j условии rhs.index()), что variant объект не будет иметь значения.

  • Если во время вызова Tjприсваивания перемещения возникает исключение , состояние содержащегося в нем значения определяется гарантией безопасности исключений Tjприсваивания перемещения; index() будет j.

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

Позвольте Tj быть типом, который определяется следующим образом: построить мнимую функцию FUN(Ti) для каждого альтернативного типа Ti. Перегрузка, FUN(Tj) выбранная разрешением перегрузки для выражения, FUN(std​::​forward<T>(​t)) определяет альтернативу, Tj которая является типом содержащегося значения после присвоения.

Effects:

  • Если *this содержит Tj, присваивает std​::​forward<T>(t) значение, содержащееся в *this. Иначе,

  • если is_­nothrow_­constructible_­v<Tj, T> || !is_­nothrow_­move_­constructible_­v<Tj> есть true, эквивалентно emplace<j>(std​::​forward<T>(t)). Иначе,

  • эквивалентно operator=(variant(std​::​forward<T>(t))).

Postconditions: holds_­alternative<Tj>(*this) есть true, с Tj выбранной мнимой функцией разрешения перегрузки, описанной выше.

Returns: *this.

Remarks: Эта функция не будет участвовать в разрешении перегрузки , если только is_­same_­v<decay_­t<T>, variant> не будет false, если только is_­assignable_­v<Tj&, T> && is_­constructible_­v<Tj, T> не будет true, и если выражение FUN(std​::​forward<T>(t))FUN того вышеупомянутый набор функций мнимых) хорошо сформированы.

[Note:

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

неправильно сформирован, поскольку оба альтернативных типа имеют одинаково жизнеспособный конструктор для аргумента. ] end note

Выражение внутри noexcept эквивалентно:

is_nothrow_assignable_v<Tj&, T> && is_nothrow_constructible_v<Tj, T>
  • Если во время присваивания std​::​forward<T>(t) значению, содержащемуся в *this, возникает исключение , состояние содержащегося значения и t определяется гарантией безопасности исключений в выражении присваивания; valueless_­by_­exception() будет false.

  • Если во время инициализации содержащегося значения возникает исключение, variant объект может не содержать значения.

23.7.3.4 Modifiers [variant.mod]

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

Позвольте I быть отсчитываемым от нуля индексом T in Types....

Effects: Эквивалентен: return emplace<I>(std​::​forward<Args>(args)...);

Remarks: Эта функция не будет участвовать в разрешении перегрузки , если is_­constructible_­v<T, Args...> не true, и T встречается ровно один раз в Types....

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

Позвольте I быть отсчитываемым от нуля индексом T in Types....

Effects: Эквивалентен: return emplace<I>(il, std​::​forward<Args>(args)...);

Remarks: Эта функция не будет участвовать в разрешении перегрузки , если is_­constructible_­v<T, initializer_­list<U>&, Args...> не true, и T встречается ровно один раз в Types....

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

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

Effects: Уничтожает текущее содержащееся значение, если valueless_­by_­exception() есть false. Затем инициализирует содержащееся значение, как если бы инициализирует значение типа напрямую без использования списка TI с аргументами std​::​forward<Args>(args)....

Postconditions: index() есть I.

Returns: Ссылка на новое содержащееся значение.

Throws: Любое исключение, возникшее во время инициализации содержащегося значения.

Remarks: Эта функция не будет участвовать в разрешении перегрузки , если is_­constructible_­v<TI, Args...> не true. Если во время инициализации содержащегося значения возникает исключение, variant возможно, значение не содержится.

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

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

Effects: Уничтожает текущее содержащееся значение, если valueless_­by_­exception() есть false. Затем инициализирует содержащееся значение, как если бы инициализирует значение типа напрямую без использования списка TI с аргументами il, std​::​forward<Args>(args)....

Postconditions: index() есть I.

Returns: Ссылка на новое содержащееся значение.

Throws: Любое исключение, возникшее во время инициализации содержащегося значения.

Remarks: Эта функция не будет участвовать в разрешении перегрузки , если is_­constructible_­v<TI, initializer_­list<U>&, Args...> не true. Если во время инициализации содержащегося значения возникает исключение, variant возможно, значение не содержится.

23.7.3.5 Value status [variant.status]

constexpr bool valueless_by_exception() const noexcept;

Effects: Возвращает false тогда и только тогда, когда variant содержит значение.

[ Note: A variant может не содержать значения, если во время присвоения или размещения с изменением типа возникает исключение. Последнее означает, что даже a variant<float, int> может стать valueless_­by_­exception(), например,

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: Если valueless_­by_­exception() есть true, возвращается variant_­npos. В противном случае возвращает отсчитываемый от нуля индекс альтернативы содержащегося значения.

23.7.3.6 Swap [variant.swap]

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

Requires: Lvalues ​​типа Ti должны быть заменяемыми ([swappable.requirements]) и is_­move_­constructible_­v<Ti> должны быть доступны true для всех i.

Effects:

  • если valueless_­by_­exception() && rhs.valueless_­by_­exception() нет эффекта. Иначе,

  • если index() == rhs.index()звонит swap(get<i>(*this), get<i>(rhs)) где i есть index(). Иначе,

  • обменивает значения rhs и *this.

Throws: Если index() == rhs.index(), какое-либо исключение, созданное swap(get<i>(*this), get<i>(rhs)) с i be index(). В противном случае любое исключение, созданное конструктором перемещения объекта Ti или Tj with i being index() and j being rhs.index().

Remarks: Если во время вызова функции выбрасывается исключение swap(get<i>(*this), get<i>(rhs)), состояния содержащихся значений of *this и of rhs определяются гарантией безопасности исключений swap для lvalues ​​of Ti with i being index(). Если во время обмена значениями *this и возникает исключение rhs, состояния значений *this и rhs определяются гарантией безопасности исключений variantконструктора перемещения. Выражение внутри noexcept эквивалентно логическому И is_­nothrow_­move_­constructible_­v<Ti> && is_­nothrow_­swappable_­v<Ti> для всех i.