std::variant<Types...>::operator=
来自cppreference.com
constexpr variant& operator=( const variant& rhs ); |
(1) | (C++17 起) |
constexpr variant& operator=( variant&& rhs ) noexcept(/* see below */); |
(2) | (C++17 起) |
(3) | ||
template< class T > variant& operator=( T&& t ) noexcept(/* see below */); |
(C++17 起) (C++20 前) |
|
template< class T > constexpr variant& operator=( T&& t ) noexcept(/* see below */); |
(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))) 。若emplace
内的复制构造抛异常,则 *this 可能变为因异常无值(valueless_by_exception
)。 - 否则,等价于 this->operator=(variant(rhs)) 。
定义此重载为被删除,除非 std::is_copy_constructible_v<T_i> 与 std::is_copy_assignable_v<T_i> 对
Types...
中所有 T_i
均为 true 。若 std::is_trivially_copy_constructible_v<T_i> 、 std::is_trivially_copy_assignable_v<T_i> 及 std::is_trivially_destructible_v<T_i> 对 Types...
中所有 T_i
均为 true 则此重载为平凡。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 变为因异常无值。
此重载只有在
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) 转换赋值:
- 确定若有对每个来自
Types...
的T_i
的虚构重载函数 F(T_i) 同时在作用域中,则重载决议是否为表达式 F(std::forward<T>(t)) 选择可选项类型T_j
,除了:
- 仅若声明 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 可能变为因异常无值。 - 否则,等价于 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<string> v1; v1 = "abc"; // OK std::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)>; 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 | 若任何成员类型不可复制则复制赋值运算符不参与重载决议 | 改为定义为被删除 |
P0602R4 | C++17 | 即使底层操作平凡,复制/移动运算符亦可能不平凡 | 要求传播平凡性 |
P0608R3 | C++17 | 转换赋值盲目地组成重载集,导致不想要的转换 | 不考虑窄化和布尔转换 |
P2231R1 | C++20 | 转换赋值不是 constexpr 而要求的操作在 C++20 中可以是 | 使之为 constexpr |
LWG 3585 | C++17 | 转换赋值有时意外地非良构,因为无可用的移动赋值 | 使之良构 |
参阅
原位构造 variant 中的值 (公开成员函数) |