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)))。若 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) 移动赋值:
  • *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) 转换赋值:
  • 确定在对 Types... 中的每个 T_i 都有一个虚构重载函数 F(T_i) 同时在作用域中时,为表达式 F(std::forward<T>(t)) 进行重载决议所选择的可选项类型 T_j,但其中:
  • 仅若声明 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>truestd::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 - 可转换成此变体的可选项之一的值

返回值

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

注解

功能特性测试 标准 功能特性
__cpp_lib_variant 202106L (C++20)
(DR)
完全 constexprstd::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 ), 其中 T 为 int\n";
    a = 2019;
    std::cout << "a" << a << '\n';
 
    std::cout << "(3) operator=( T&& t ), 其中 T 为 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 ), 其中 T 为 int
a: { 2019 };
 
(3) operator=( T&& t ), 其中 T 为 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 中的值
(公开成员函数)