std::variant介绍
在标头
{/card-list-item}
{card-list-item}template <class... Types>
class variant; (C++17 起)
{/card-list-item}
类模板 std::variant 表示一个类型安全的联合体。 std::variant 的一个实例在任意时刻要么保有其一个可选类型之一的值,要么在错误情况下无值(此状态难以达成,见 valueless_by_exception )。
与联合体在聚合初始化中的行为一致, 若 variant 保有某个对象类型 T 的值,则直接于 variant 的对象表示中分配 T 的对象表示。不允许 variant 分配额外的(动态)内存。
variant 不容许保有引用、数组,或类型 void 。空 variant 亦为病式(可用 std::variant<std::monostate> 代替)。
variant 容许保有同一类型多于一次,而且可保有同一类型的不同 cv 限定版本。
同联合体,默认构造的 variant 保有其首个选项的值,除非该选项不是可默认构造的(该情况下 variant 亦非可默认构造:能用辅助类 std::monostate 使这种 variant 可默认构造)。
Types - 可存储于此 variant 中的类型。所有类型必须满足可析构 (Destructible) 要求(特别是不允许数组类型和非对象类型)。
std::variant的简单使用
#include <iostream>
#include <variant>
using var_T = std::variant<int,double,char>;
int main() {
var_T v;
v = 1;//给v复制int
std::cout << std::get<int>(v) << ' ' << v.index() << '\n';
v = 2.5;//给v赋值double
std::cout << std::get<double>(v) << ' ' << v.index() << '\n';
v = 'A';//给v赋值char
std::cout << std::get<char>(v) << ' ' << v.index() << '\n';
v = "Hello cremache!" //error,v不能储存字符串类型
return 0;
}
输出:
1 0
2.5 1
A 2
分析:
示例给的 variant 可以储存int,double,char类型的数据,如果我们需要将v储存的值输出,那么就需要用到std::get<type>(var)
,但是这种输出需要你显式指出v储存的值的类型,auto
在这里是不能使用的,当然除了具体的数据类型,你也可以使用 0 代表 int ,1 代表 double ,2 代表 char(具体请根据你自己的定义,这里的012是根据我的定义)。
除了std::get()
C++还提供了std::get_if()
,get_if 同样需要显式指出数据类型,get_if的参数是指向 variant 的指针,返回值是指向存储于被指向的 variant 中值的指针,错误时为空指针。index()
可以返回 variant 当前所保有的可选项的零基下标。
若 variant 因异常无值( valueless_by_exception ),则返回 variant_npos 。
通俗来说就是0是int,1是double(仅限示例)。
在std::vector中使用std::variant
#include <iostream>
#include <variant>
#include <vector>
using var_T = std::variant<int,double,char>;
template<class... Ts> struct overloaded : Ts... {using Ts::operator()...;};
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main() {
std::vector<var_T> n{ 2.5 , 'A', 1};
for(auto v : n)
{
std::visit(overloaded{
[](int arg) { std::cout << arg; },
[](double arg) {std::cout << std::fixed << arg;},
[](char arg) {std::cout << arg;},
},v);
std::cout << '\n';
}
return 0;
}
输出:
2.50000
A
1
这是cppreference给出的写法,我们需要使用visit()
函数来访问数据,但是这种写法非常麻烦,这里有一个难懂的overloaded 在 Stack Overflow 中有大佬对overloaded做出解释:
overloaded
{
[](int arg) { std::cout << arg; },
[](double arg) {std::cout << std::fixed << arg;},
[](char arg) {std::cout << arg;},
}
在示例代码中还有另一个有趣的新C++17特性:基类的聚合初始化。
也就是说...当你写下如下代码时:
overloaded
{
[](int arg) { std::cout << arg; },
[](double arg) {std::cout << std::fixed << arg;},
[](char arg) {std::cout << arg;},
}
你是在初始化overloaded的三个基类时传递了三个lambda函数。
在C++17之前,只有在编写了显式构造函数时才能这样做。从C++17开始,它可以自动工作。
但是这种写法我们可以简化!
#include <iostream>
#include <variant>
#include <vector>
using var_T = std::variant<int,double,char>;
int main() {
std::vector<var_T> n{ 2.5 , 'A', 1};
for(auto v : n)
{
std::visit([](auto arg){std::cout << arg;},v);
std::cout << '\n';
}
return 0;
}
直接使用使用auto推导类型,就不用我们分情况写了。当然,具体操作还是得根据具体情况使用。
评论 (0)