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

来自cppreference.com
< cpp‎ | utility‎ | variant
 
 
 
 
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 的变体。
3) 移动构造函数。若 other 并非 valueless_by_exception,则构造一个保有与 other 相同可选项的 variant 并以 std::move(*std::get_if<other.index()>(std::addressof(other)))直接初始化所含值。否则,初始化一个 valueless_by_exception 的变体。
4) 转换构造函数。对 Types... 中的每个 T_i 有一个虚设的函数 F(T_i) 的重载,根据表达式 F(std::forward<T>(t)) 进行重载决议得到 T_j,构造一个持有它的 variant,但不考虑窄化转换。

正式而言:

  • 只有当声明 T_i x[] = { std::forward<T>(t) }; 对某个虚设变量 x 有效时,才考虑 F(T_i)
如同用直接非列表初始化从 std::forward<T>(t)直接初始化所含值。
std::variant<std::string> v("abc"); // OK
std::variant<std::string, std::string> w("abc"); // 非良构
std::variant<std::string, const char*> x("abc"); // OK:选择 const char*
std::variant<std::string, bool> y("abc"); // OK:选择 string;bool 不是候选
std::variant<float, long, double> z = 0; // OK:保有 long
                                         // float 与 double 不是候选
5) 构造一个有指定可选项类型 Tvariant 并以实参 std::forward<Args>(args)... 初始化所含值。
  • T 的被选择构造函数是 constexpr 构造函数,则此构造函数亦为 constexpr 构造函数。
  • 此重载只有在 Types... 中正好出现一次 Tstd::is_constructible_v<T, Args...>true 时才会参与重载决议。
6) 构造一个有指定可选项类型 Tvariant 并以实参 il, std::forward<Args>(args)... 初始化所含值。
  • T 的被选择构造函数是 constexpr 构造函数,则此构造函数亦为 constexpr 构造函数。
  • 此重载只有在 Types... 中正好出现一次 Tstd::is_constructible_v<T, initializer_list<U>&, Args...>true 时才会参与重载决议。
7) 构造一个有索引 I 所指定的可选项类型 T_ivariant 并以实参 std::forward<Args>(args)... 初始化所含值。
  • T_i 的被选择构造函数是 constexpr 构造函数,则此构造函数亦为 constexpr 构造函数。
  • 此重载只有在 I < sizeof...(Types)std::is_constructible_v<T_i, Args...> 皆为 true 时才会参与重载决议。
8) 构造一个有索引 I 所指定的可选项类型 T_ivariant 并以实参 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) 可能抛出移动构造 Types... 中的任何 T_i 时所抛的任何异常。
noexcept 说明:  
noexcept( (std::is_nothrow_move_constructible_v<Types> && ...))
4) 可能抛出初始化所选可选项 T_j 时所抛的任何异常。
noexcept 说明:  
5-8) 可能抛出调用所选可选项的所选构造函数时所抛的任何异常。

示例

#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));
    std::cout << "6) " << std::get<vector_t>(var6) << '\n';
}

输出:

1) STR
2) 42
3) AAAA
4) { 1 2 3 4 5 }
5) ABC
6) { 42 42 42 42 }

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告应用于出版时的行为正确行为
LWG 2901C++17提供具分配器构造函数但 variant 不能正确支持分配器移除构造函数
P0739R0C++17转换构造函数模板与类模板实参推导交互困难添加了约束
LWG 3024C++17若任何成员类型非可复制,则复制构造函数不参与重载决议改为定义为被弃置
P0602R4C++17即使底层构造函数平凡,复制/移动构造函数亦可为非平凡要求传播平凡性
P0608R3C++17转换构造函数盲目地组成重载集,导致不想要的转换不考虑窄化与布尔转换
P1957R2C++17bool 的转换构造函数不允许隐式转换指针向 bool 转换是窄化,且转换构造函数对 bool 没有例外