Namespaces
Variants

std::variant<Types...>:: variant

From cppreference.net
Utilities library
constexpr variant ( ) noexcept ( /* 见下文 */ ) ;
(1) (C++17 起)
constexpr variant ( const variant & other ) ;
(2) (C++17 起)
constexpr variant ( variant && other ) noexcept ( /* 见下文 */ ) ;
(3) (C++17 起)
template < class T >
constexpr variant ( T && t ) noexcept ( /* 见下文 */ ) ;
(4) (C++17 起)
template < class T,

class ... Args >
constexpr explicit variant ( std:: in_place_type_t < T > ,

Args && ... args ) ;
(5) (C++17 起)
template < class T,

class U,
class ... Args >
constexpr explicit variant ( std:: in_place_type_t < T > ,
std:: initializer_list < U > il,

Args && ... args ) ;
(6) (C++17 起)
template < std:: size_t I,

class ... Args >
constexpr explicit variant ( std:: in_place_index_t < I > ,

Args && ... args ) ;
(7) (C++17 起)
template < std:: size_t I,

class U,
class ... Args >
constexpr explicit variant ( std:: in_place_index_t < I > ,
std:: initializer_list < U > il,

Args && ... args ) ;
(8) (C++17 起)

构造一个新的 variant 对象。

1) 默认构造函数。构造一个持有首个可选类型的 值初始化 值的 variant (此时 index() 为零)。
2) 复制构造函数。若 other valueless_by_exception ,则构造一个持有与 other 相同可选项的 variant ,并通过 * std:: get_if < other. index ( ) > ( std:: addressof ( other ) ) 对所含值进行 直接初始化 。否则,初始化一个 valueless_by_exception 的 variant。
3) 移动构造函数。如果 other 不处于 valueless_by_exception 状态,则构造一个持有与 other 相同可选项的 variant ,并通过 直接初始化 使用 std :: move ( * std:: get_if < other. index ( ) > ( std:: addressof ( other ) ) ) 初始化所含值。否则,初始化一个 valueless_by_exception 状态的 variant。
4) 转换构造函数。构造一个持有备选类型 T_j variant ,该类型将通过重载决议为表达式 F ( std:: forward < T > ( t ) ) 选择,如同对 Types... 中的每个 T_i 都存在虚构函数重载 F(T_i) ,但不考虑窄化转换。

形式化定义:

  • 仅当声明 T_i x [ ] = { std:: forward < T > ( t ) } ; 对某个虚构变量 x 有效时,才会考虑重载 F ( T_i )
直接初始化 所容纳的值,如同通过从 std:: forward < T > ( t ) 进行直接非列表初始化。
std::variant<std::string> v("abc"); // 正确
std::variant<std::string, std::string> w("abc"); // 非良构
std::variant<std::string, const char*> x("abc"); // 正确,选择 const char*
std::variant<std::string, bool> y("abc"); // 正确,选择 string;bool 不是候选类型
std::variant<float, long, double> z = 0; // 正确,持有 long
                                         // float 和 double 不是候选类型
5) 构造一个指定可选项类型 T variant ,并使用参数 std:: forward < Args > ( args ) ... 初始化所含值。
  • 如果 T 的选定构造函数是 constexpr 构造函数,则此构造函数也是 constexpr 构造函数。
  • 此重载仅在以下条件下参与重载决议: Types... T 仅出现一次,且 std:: is_constructible_v < T, Args... > true
6) 构造一个指定类型 T variant ,并使用参数 il, std:: forward < Args > ( args ) ... 初始化所容纳的值。
  • 如果 T 的选定构造函数是 constexpr 构造函数,则此构造函数也是 constexpr 构造函数。
  • 仅当 Types... T 仅出现一次,且 std:: is_constructible_v < T, initializer_list < U > & , Args... > true 时,此重载才会参与重载决议。
7) 构造一个由索引 I 指定的备选类型 T_i variant ,并使用参数 std:: forward < Args > ( args ) ... 初始化所含值。
  • T_i 选择的构造函数是 constexpr 构造函数,则此构造函数同样是 constexpr 构造函数。
  • 此重载仅当 I < sizeof... ( Types ) std:: is_constructible_v < T_i, Args... > true 时参与重载决议。
8) 构造一个由索引 I 指定的可选类型 T_i variant ,并使用参数 il, std:: forward < Args > ( args ) ... 初始化所含值。
  • 如果 T_i 选择的构造函数是 constexpr 构造函数,则此构造函数也是 constexpr 构造函数。
  • 此重载仅在满足 I < sizeof... ( Types ) std:: is_constructible_v < T_i, std:: initializer_list < U > & , Args... > true 时参与重载决议。

目录

参数

other - 另一个待复制/移动其内含值的 variant 对象
t - 用于初始化内含值的值
args... - 用于初始化内含值的参数
il - 用于初始化内含值的初始化列表

异常

1) 可能抛出首个可选值初始化抛出的任何异常。
2) 可能抛出直接初始化 Types... 中任意 T_i 时抛出的任何异常。
3) 可能抛出移动构造任意 T_i 时抛出的异常。
noexcept 规范:
noexcept ( ( std:: is_nothrow_move_constructible_v < Types > && ... ) )
4) 可能抛出所选替代项 T_j 初始化过程中抛出的任何异常。
noexcept 规范:
noexcept ( std:: is_nothrow_constructible_v < T_j, T > )
5-8) 可能抛出通过调用所选备选项的选定构造函数而抛出的任何异常。

注释

MSVC STL 最初将 P0608R3 视为 C++20 的变更。自 VS 2022 17.12 起,MSVC STL 也将 P0608R3 视为针对 C++17 的缺陷报告。

示例

#include <cassert>
#include <iostream>
#include <string>
#include <variant>
#include <vector>
using vector_t = std::vector<int>;
auto& operator<<(auto& out, const vector_t& v)
{
    out << "{ ";
    for (int e : v)
        out << e << ' ';
    return out << '}';
}
int main()
{
    // 值初始化第一个备选项
    std::variant<int, std::string> var0;
    assert(std::holds_alternative<int>(var0) and
           var0.index() == 0 and
           std::get<int>(var0) == 0);
    // 使用 std::string{"STR"} 初始化第一个备选项
    std::variant<std::string, int> var1{"STR"};
    assert(var1.index() == 0);
    std::cout << "1) " << std::get<std::string>(var1) << '\n';
    // 使用 int == 42 初始化第二个备选项
    std::variant<std::string, int> var2{42};
    assert(std::holds_alternative<int>(var2));
    std::cout << "2) " << std::get<int>(var2) << '\n';
    // 使用 std::string{4, 'A'} 初始化第一个备选项
    std::variant<std::string, vector_t, float> var3
    {
        std::in_place_type<std::string>, 4, 'A'
    };
    assert(var3.index() == 0);
    std::cout << "3) " << std::get<std::string>(var3) << '\n';
    // 使用 std::vector{1,2,3,4,5} 初始化第二个备选项
    std::variant<std::string, vector_t, char> var4
    {
        std::in_place_type<vector_t>, {1, 2, 3, 4, 5}
    };
    assert(var4.index() == 1);
    std::cout << "4) " << std::get<vector_t>(var4) << '\n';
    // 使用 std::string{"ABCDE", 3} 初始化第一个备选项
    std::variant<std::string, vector_t, bool> var5 {std::in_place_index<0>, "ABCDE", 3};
    assert(var5.index() == 0);
    std::cout << "5) " << std::get<std::string>(var5) << '\n';
    // 使用 std::vector(4, 42) 初始化第二个备选项
    std::variant<std::string, vector_t, char> var6 {std::in_place_index<1>, 4, 42};
    assert(std::holds_alternative<vector_t>(var6)

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 适用版本 发布时行为 修正后行为
LWG 2901 C++17 提供了分配器感知构造函数但
variant 无法正确支持分配器
移除构造函数
P0739R0 C++17 转换构造函数模板与类模板
参数推导配合不佳
添加约束条件
LWG 3024 C++17 若任何成员类型不可复制则
拷贝构造函数不参与重载决议
改为定义为删除
P0602R4 C++17 即使底层构造函数是平凡
拷贝/移动构造函数也可能非平凡
要求传播平凡性
P0608R3 C++17 转换构造函数盲目组合重载集
导致非预期的转换
不考虑窄化和布尔转换
P1957R2 C++17 bool 的转换构造函数不允许
隐式转换
指针到 bool 的转换属于窄化且
转换构造函数未对 bool
提供特殊处理