命名空间
变体
操作

std::variant<Types...>::operator=

来自 cppreference.com
< cpp‎ | utility‎ | variant
 
 
实用程序库
语言支持
类型支持 (基本类型,RTTI)
库特性测试宏 (C++20)
动态内存管理
程序实用程序
协程支持 (C++20)
可变参数函数
调试支持
(C++26)
三路比较
(C++20)
(C++20)(C++20)(C++20)
(C++20)(C++20)(C++20)
通用实用程序
日期和时间
函数对象
格式化库 (C++20)
(C++11)
关系运算符 (C++20 中已弃用)
整数比较函数
(C++20)(C++20)(C++20)   
(C++20)
交换类型操作
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
通用词汇类型
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
基本字符串转换
(C++17)
(C++17)

 
 
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) 复制赋值
  • 如果 *thisrhs 都是因异常而无效的,则什么也不做。
  • 否则,如果 rhs 无效,但 *this 有效,则销毁 *this 中包含的值并使其无效。
  • 否则,如果 rhs 持有的替代方案与 *this 相同,则将 rhs 中包含的值分配给 *this 中包含的值。如果抛出异常,则 *this 不会变成无效的:该值取决于替代方案的复制赋值的异常安全保证。
  • 否则,如果 rhs 持有的替代方案是不可抛出复制构造的,或者不是 不可抛出移动构造的(分别由 std::is_nothrow_copy_constructiblestd::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) 移动赋值
  • 如果 *thisrhs 都是因异常而无效的,则什么也不做。
  • 否则,如果 rhs 无效,但 *this 有效,则销毁 *this 中包含的值并使其无效。
  • 否则,如果 rhs 持有的替代方案与 *this 相同,则将 std::move(*std::get_if<j>(std::addressof(rhs))) 分配给 *this 中包含的值,其中 jindex()。如果抛出异常,则 *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)

此重载仅当 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>) && ...))
3)
noexcept 规范:  
noexcept(std::is_nothrow_assignable_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 中就地构造一个值
(公共成员函数) [编辑]