std::variant<Types...>::operator=
来自 cppreference.com
constexpr variant& operator=( const variant& rhs ); |
(1) | (自 C++17 起) |
constexpr variant& operator=( variant&& rhs ) noexcept(/* 见下文 */); |
(2) | (自 C++17 起) |
template< class T > variant& operator=( T&& t ) noexcept(/* 见下文 */); |
(3) | (自 C++17 起) (自 C++20 起为 constexpr) |
将一个新值分配给现有的 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,对于
Types...
中的所有 T_i
。如果 std::is_trivially_copy_constructible_v<T_i>,std::is_trivially_copy_assignable_v<T_i> 和 std::is_trivially_destructible_v<T_i> 都为 true,对于 Types...
中的所有 T_i
,则此重载是平凡的。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> 对
Types...
中的所有 T_i
均为 true 时才参与重载解析。如果 std::is_trivially_move_constructible_v<T_i>、std::is_trivially_move_assignable_v<T_i> 和 std::is_trivially_destructible_v<T_i> 对 Types...
中的所有 T_i
均为 true,则此重载是平凡的。3) 转换赋值。
- 确定备用类型
T_j
,如果存在一个虚构函数 F(T_i) 的重载,对于Types...
中的每个T_i
,在相同作用域内同时存在,则通过重载解析来选择该备用类型T_j
,用于表达式 F(std::forward<T>(t)),但
- 仅当声明 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))。如果在
emplace
内部初始化时抛出异常,*this 可能变为valueless_by_exception
。 - 否则,等效于 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 | - | 可转换为变体其中一个备用类型的值 |
[编辑] 返回值
*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>)
[编辑] 注释
特性测试 宏 | 值 | Std | 特性 |
---|---|---|---|
__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++ 标准。
DR | 应用于 | 已发布的行为 | 正确行为 |
---|---|---|---|
LWG 3024 | C++17 | 复制赋值运算符不参与重载解析 如果任何成员类型不可复制 |
定义为已删除,而不是 |
LWG 3585 | C++17 | 转换赋值有时意外地格式不佳 因为没有可用的移动赋值 |
使其格式良好 |
P0602R4 | C++17 | 复制/移动赋值可能不是平凡的 即使基础操作是平凡的 |
要求传播平凡性 |
P0608R3 | C++17 | 转换赋值盲目地组装一个重载集合, 导致意外的转换 |
缩窄转换和布尔转换 未考虑 |
P2231R1 | C++20 | 转换赋值 (3) 不是 constexpr 而所需操作可以在 C++20 中为 constexpr |
使其为 constexpr |
[编辑] 参见
在 variant 中就地构造一个值(公共成员函数) |