std::variant<Types...>::variant
来自cppreference.com
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 > |
(5) | (C++17 起) |
template< class T, class U, |
(6) | (C++17 起) |
template< std::size_t I, class... Args > |
(7) | (C++17 起) |
template< std::size_t I, class U, |
(8) | (C++17 起) |
构造新的 variant
对象。
1) 默认构造函数。构造
variant
,保有首个可选项(index() 为零)的值初始化的值。
- 当且仅当可选项类型
T_0
的值初始化满足 constexpr 函数的要求时,此构造函数为 constexpr。 - 此重载只有在 std::is_default_constructible_v<T_0> 为 true 时才会参与重载决议。
2) 复制构造函数。若 other 并非 valueless_by_exception,则构造一个保有与 other 相同可选项的
variant
,并以 *std::get_if<other.index()>(std::addressof(other)) 直接初始化所含值。否则,初始化一个 valueless_by_exception 的变体。
- 此构造函数定义为被弃置,除非 std::is_copy_constructible_v<T_i> 对于 Types... 中的所有
T_i
均为 true。 - 若 std::is_trivially_copy_constructible_v<T_i> 对 Types... 中的所有
T_i
为 true 则它为平凡。
3) 移动构造函数。若 other 并非 valueless_by_exception,则构造一个保有与
other
相同可选项的 variant
并以 std::move(*std::get_if<other.index()>(std::addressof(other))) 直接初始化所含值。否则,初始化一个 valueless_by_exception 的变体。
- 此重载只有在 std::is_move_constructible_v<T_i> 对于 Types... 中的所有
T_i
均为 true 时才会参与重载决议。 - 若 std::is_trivially_move_constructible_v<T_i> 对 Types... 中的所有
T_i
均为 true 则它为平凡。
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)。
- 只有当声明 T_i x[] = { std::forward<T>(t) }; 对某个虚设变量
如同用直接非列表初始化从 std::forward<T>(t) 直接初始化所含值。
- 此重载只有在
- sizeof...(Types) > 0,
- std::decay_t<T> (C++20 前)std::remove_cvref_t<T> (C++20 起) 既不与 variant 为同一类型,也不是 std::in_place_type_t 或 std::in_place_index_t 的特化,
- std::is_constructible_v<T_j, T> 为 true,
- 且表达式 F(std::forward<T>(t))(令
F
为上述虚构函数的重载集)为良构时才会参与重载决议。
- 若
T_j
的被选择构造函数为 constexpr 构造函数,则此构造函数为 constexpr 构造函数。
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) 构造一个有指定可选项类型
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) 可能抛出首个可选项的值初始化所抛的任何异常。
noexcept 说明:
noexcept(std::is_nothrow_default_constructible_v<T_0>)
2) 可能抛出直接初始化 Types... 中的任何
T_i
时所抛的任何异常。3) 可能抛出移动构造 Types... 中的任何
T_i
时所抛的任何异常。noexcept 说明:
noexcept( (std::is_nothrow_move_constructible_v<Types> && ...))
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 2901 | C++17 | 提供具分配器构造函数但 variant 不能正确支持分配器
|
移除构造函数 |
P0739R0 | C++17 | 转换构造函数模板与类模板实参推导交互困难 | 添加了约束 |
LWG 3024 | C++17 | 若任何成员类型非可复制,则复制构造函数不参与重载决议 | 改为定义为被弃置 |
P0602R4 | C++17 | 即使底层构造函数平凡,复制/移动构造函数亦可为非平凡 | 要求传播平凡性 |
P0608R3 | C++17 | 转换构造函数盲目地组成重载集,导致不想要的转换 | 不考虑窄化与布尔转换 |
P1957R2 | C++17 | 对 bool 的转换构造函数不允许隐式转换 | 指针向 bool 转换是窄化,且转换构造函数对 bool 没有例外 |