命名空间
变体
操作

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

来自 cppreference.cn
< cpp‎ | utility‎ | variant
 
 
工具库
语言支持
类型支持 (基本类型,RTTI)
库特性测试宏 (C++20 起)
程序工具
可变参数函数
(C++11)
(C++20)
协程支持 (C++20 起)
契约支持 (C++26 起)
三路比较
(C++20 起)(C++20 起)
(C++20)
(C++20)
(C++20)
(C++20)
(C++20)
(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++20 起)(C++20 起)
(C++20)
交换类型操作
(C++20)
(C++14)
(C++11)
(C++23)
(C++11)
(C++23)
(C++11)
(C++11)
(C++17)
常用词汇类型
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++11)
(C++17)
(C++17)
(C++23)



 
 
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 起)
(constexpr 自 C++20 起)

为现有的 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)))。 如果在 emplace 内的复制构造上抛出异常,*this 可能会变为 valueless_by_exception
  • 否则,等效于 this->operator=(variant(rhs))
除非对于 Types... 中的所有 T_istd::is_copy_constructible_v<T_i>std::is_copy_assignable_v<T_i> 均为 true,否则此重载被定义为已删除。 如果对于 Types... 中的所有 T_istd::is_trivially_copy_constructible_v<T_i>std::is_trivially_copy_assignable_v<T_i>std::is_trivially_destructible_v<T_i> 均为 true,则此重载是平凡的。
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
仅当对于 Types... 中的所有 T_istd::is_move_constructible_v<T_i>std::is_move_assignable_v<T_i> 均为 true 时,此重载才参与重载解析。 如果对于 Types... 中的所有 T_istd::is_trivially_move_constructible_v<T_i>std::is_trivially_move_assignable_v<T_i>std::is_trivially_destructible_v<T_i> 均为 true,则此重载是平凡的。
3) 转换赋值。
  • 确定备选类型 T_j,如果对于 Types... 中的每个 T_i,在同一作用域中都存在虚构函数 F(T_i) 的重载,则将为表达式 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

内容

[edit] 参数

rhs - 另一个 variant
t - 可转换为变体的备选项之一的值

[edit] 返回值

*this

[edit] 异常

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>)

[edit] 注解

特性测试 Std 特性
__cpp_lib_variant 202106L (C++20)
(DR)
完全 constexpr std::variant 3

[edit] 示例

#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: ""

[edit] 缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的 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

[edit] 参见

variant 中就地构造值
(公共成员函数) [编辑]