std::variant<Types...>::operator=
Материал из cppreference.com
constexpr variant& operator=(const variant& rhs ); | (1) | (начиная с C++17) |
constexpr variant& operator=( variant&& rhs )noexcept(/* смотрите ниже */); | (2) | (начиная с C++17) |
(3) | ||
template<class T > variant& operator=( T&& t )noexcept(/* смотрите ниже */); | (начиная с C++17) (до C++20) | |
template<class T > constexpr variant& operator=( T&& t )noexcept(/* смотрите ниже */); | (начиная с C++20) | |
Присваивает новое значение существующему объекту variant
.
1) Присваивание копированием:
- Если оба *this и rhs не имеют значения в силу исключения, ничего не делает.
- Иначе, если rhs не имеет значения, а *this имеет, уничтожает значение, содержащееся в *this, и делает не содержащим значение.
- Иначе, если rhs содержит ту же альтернативу, что и *this, присваивает значение, содержащееся в rhs, значению, содержащемуся в *this. Если генерируется исключение, *this не становится не имеющим значение: значение зависит от гарантии безопасности исключения присваивания копированием альтернативного значения.
- Иначе, если альтернатива, удерживаемая rhs является либо создаваемой копированием без исключения, либо создаваемой перемещением с исключениями (как определено std::is_nothrow_copy_constructible и std::is_nothrow_move_constructible, соответственно), эквивалентно this->emplace<rhs.index()>(*std::get_if<rhs.index()>(std::addressof(rhs))). *this может стать
valueless_by_exception
, если возникнет исключение во время создания копированием внутриemplace
. - Иначе эквивалентно this->operator=(variant(rhs)).
Эта перегрузка определяется как удалённая, если только std::is_copy_constructible_v<T_i> и std::is_copy_assignable_v<T_i> оба не равны true для всех
T_i
в Types...
. Эта перегрузка тривиальна, если std::is_trivially_copy_constructible_v<T_i>,std::is_trivially_copy_assignable_v<T_i> и std::is_trivially_destructible_v<T_i> все равны true для всех T_i
в Types...
.2) Присваивание перемещением:
- Если оба *this и rhs не имеют значения из-за исключения, ничего не делает
- Иначе, если rhs не имеет значения, а *this имеет, уничтожает значение, содержащееся в *this, и делает его не содержащим значение.
- Иначе, если rhs содержит ту же альтернативу, что и *this, присваивает std::move(*std::get_if<j>(std::addressof(rhs))) содержащемуся в *this значению, где
j
равноindex()
. Если генерируется исключение, *this не становится не имеющим значение: значение зависит от гарантии безопасности исключения присваивания перемещением альтернативного значения. - Иначе (если rhs и *this содержат разные альтернативы), эквивалентно this->emplace<rhs.index()>(std::move(*std::get_if<rhs.index()>(std::addressof(rhs)))). Если конструктор перемещения
T_i
генерирует исключение, *this становитсяvalueless_by_exception
.
Эта перегрузка участвует в разрешении перегрузки, только если std::is_move_constructible_v<T_i> и std::is_move_assignable_v<T_i> оба равны true для всех
T_i
в Types...
. Эта перегрузка тривиальна, если std::is_trivially_move_constructible_v<T_i>, std::is_trivially_move_assignable_v<T_i> и std::is_trivially_destructible_v<T_i> все равны true для всех T_i
в Types...
.3) Преобразующее присваивание.
- Определяет альтернативный тип
T_j
, который был бы выбран при разрешении перегрузки для выражения F(std::forward<T>(t)) в случае одновременной перегрузки мнимой функции F(T_i) для каждогоT_i
изTypes...
в области видимости, за исключением того, что:
- Перегрузка F(T_i) рассматривается только в том случае, если объявление T_i x[]={std::forward<T>(t)}; допустимо для некоторой искусственной переменной
x
;
- Перегрузка F(T_i) рассматривается только в том случае, если объявление T_i x[]={std::forward<T>(t)}; допустимо для некоторой искусственной переменной
- Если *this уже содержит
T_j
, присваивает std::forward<T>(t) значению, содержащемуся в *this. Если генерируется исключение, *this не становится не содержащим значение: значение зависит от гарантии безопасности исключения вызываемого присваивания. - Иначе, если std::is_nothrow_constructible_v<T_j, T>||!std::is_nothrow_move_constructible_v<T_j> равно true, эквивалентно this->emplace<j>(std::forward<T>(t)). *this может стать
valueless_by_exception
, если при инициализации внутриemplace
возникнет исключение. - Иначе эквивалентно this->emplace<j>(T_j(std::forward<T>(t))).
Эта перегрузка участвует в разрешении перегрузки, только если std::decay_t<T>(до C++20)std::remove_cvref_t<T>(начиная с C++20) не имеет того же типа, что и variant и std::is_assignable_v<T_j&, T> равно true и std::is_constructible_v<T_j, T> равно true, а выражение F(std::forward<T>(t)) (где F это вышеупомянутый набор мнимых функций) корректно.
std::variant<std::string> v1; v1 ="abc";// OKstd::variant<std::string, std::string> v2; v2 ="abc";// Ошибкаstd::variant<std::string, bool> v3; v3 ="abc";// OK, выбирает string; bool не является кандидатомstd::variant<float, long, double> v4;// содержит float v4 =0;// OK, содержит long; float и double не являются кандидатами
Содержание |
[править]Параметры
rhs | — | другой variant |
t | — | значение, конвертируемое в одну из альтернатив объекта variant |
[править]Возвращаемое значение
*this
[править]Исключения
1) Может генерировать любое исключение, вызванное присваиванием и инициализацией копированием/перемещением любой альтернативы.
2)
спецификация noexcept:
noexcept(((std::is_nothrow_move_constructible_v<Types>&&
std::is_nothrow_move_assignable_v<Types>)&& ...))
std::is_nothrow_move_assignable_v<Types>)&& ...))
3)
спецификация noexcept:
noexcept(std::is_nothrow_assignable_v<T_j&, T>&&std::is_nothrow_constructible_v<T_j, T>)
[править]Пример
Запустить этот код
#include <iomanip>#include <iostream>#include <string>#include <type_traits>#include <variant> std::ostream& operator<<(std::ostream& os, std::variant<int, std::string>const& va){ os <<": { "; std::visit([&](auto&& arg){using T =std::decay_t<decltype(arg)>;ifconstexpr(std::is_same_v<T, int>) os << arg;elseifconstexpr(std::is_same_v<T, std::string>) os <<std::quoted(arg);}, va); return os <<" };\n";} int main(){std::variant<int, std::string> a{2017}, b{"CppCon"};std::cout<<"a"<< a <<"b"<< b <<'\n'; std::cout<<"(1) operator=( const variant& rhs )\n"; a = b;std::cout<<"a"<< a <<"b"<< b <<'\n'; std::cout<<"(2) operator=( variant&& rhs )\n"; a = std::move(b);std::cout<<"a"<< a <<"b"<< b <<'\n'; std::cout<<"(3) operator=( T&& t ), где T является int\n"; a =2019;std::cout<<"a"<< a <<'\n'; std::cout<<"(3) operator=( T&& t ), где T является std::string\n";std::string s{"CppNow"};std::cout<<"s: "<<std::quoted(s)<<'\n'; a = std::move(s);std::cout<<"a"<< a <<"s: "<<std::quoted(s)<<'\n';}
Возможный вывод:
a: { 2017 }; b: { "CppCon" }; (1) operator=( const variant& rhs ) a: { "CppCon" }; b: { "CppCon" }; (2) operator=( variant&& rhs ) a: { "CppCon" }; b: { "" }; (3) operator=( T&& t ), где T является int a: { 2019 }; (3) operator=( T&& t ), где T является std::string s: "CppNow" a: { "CppNow" }; s: ""
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
LWG 3024 | C++17 | оператор присваивания копированием не участвует в разрешении перегрузки, если какой-либо тип элемента не копируемый | вместо этого определяется как удалённый |
WG не указан | C++17 | присваивание копированием/перемещением может быть нетривиальным, даже если базовые операции тривиальны | требуется для распространения тривиальности |
WG не указан | C++17 | преобразующее присваивание вслепую собирает набор перегрузок, что приводит к непреднамеренным преобразованиям | сужение и логические преобразования не учитываются |
WG не указан | C++20 | преобразующее присваивание не было constexpr, в то время как необходимые операции могут быть в C++20 | сделано constexpr |
LWG 3585 | c++17 | преобразующее присваивание иногда было некорректным, потому что не было доступного присваивания перемещением | сделано корректным |
[править]Смотрите также
создаёт значение в variant на месте (public функция-элемент) |