std::variant<类型...>::operator=
来自 zh.cppreference.net
constexpr variant& operator=( const variant& rhs );
|
(1) | (C++17 起) |
constexpr variant& operator=( variant&& rhs ) noexcept(/* see below */);
|
(2) | (C++17 起) |
template< class T >
variant& operator=( T&& t ) noexcept(/* see below */);
|
(3) | (C++17 起) (C++20 起为 constexpr) |
为现有 variant 对象赋予新值。
1) 复制赋值:
- 如果
*this与rhs均为因异常无值,则不进行操作。 - 否则,若
rhs无值,但*this有值,则销毁*this所含的值并使其变为无值状态。 - 否则,如果
rhs持有与*this相同的可选项,则将rhs所含的值赋给*this所含的值。若抛出异常,*this不会变为无值:其值取决于该可选项复制赋值的异常安全保证。 - 否则,如果
rhs持有的可选项不是 noexcept 可复制构造,或者 不是 noexcept 可移动构造(分别由 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;
- 仅当对于某个发明的变量
- 如果
*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"; // OK
std::variant<std::string, std::string> v2;
v2 = "abc"; // Error
std::variant <std::string, bool> v3;
v3 = "abc"; // OK, chooses string; bool is not a candidate
std::variant<float, long, double> v4; // holds float
v4 = 0; // OK, holds long; float and double are not candidates
参数
| 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 > )
std:: is_nothrow_constructible_v < T_j, T > )
注释
| 功能测试 宏 | 值 | 标准 | 特性 |
|---|---|---|---|
__cpp_lib_variant
|
202106L
|
(C++20)
(DR) |
完全
constexpr
化的
std::variant
(
3
)
|
示例
运行此代码
#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)>; if constexpr (std::is_same_v<T, int>) os << arg; else if constexpr (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 ), where T is int\n"; a = 2019; std::cout << "a" << a << '\n'; std::cout << "(3) operator=( T&& t ), where T is 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 ), where T is int
a: { 2019 };
(3) operator=( T&& t ), where T is std::string
s: "CppNow"
a: { "CppNow" };
s: ""
缺陷报告
下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
| 缺陷报告 | 应用于 | 发布时的行为 | 正确行为 |
|---|---|---|---|
| LWG 3024 | C++17 |
若任何成员类型不可复制时
拷贝赋值运算符不参与重载决议 |
改为定义为删除状态 |
| LWG 3585 | C++17 |
由于没有可用的移动赋值操作
转换赋值有时意外地形成病式表达式 |
改为良构 |
| P0602R4 | C++17 |
即使底层操作是平凡的
拷贝/移动赋值可能不是平凡的 |
要求传播平凡性 |
| P0608R3 | C++17 |
转换赋值盲目组合重载集
导致非预期的转换 |
不考虑窄化和布尔转换 |
| P2231R1 | C++20 |
转换赋值
(
3
)
不是
constexpr
而在 C++20 中所需操作可以是 constexpr |
改为 constexpr |
参见
在
variant
中就地构造值
(公开成员函数) |