上一篇文章讲到了 union,union
union存在很多问题,因此C++17设计了一个新的variant替代原来的union。
union的问题
- 无法知道当前使用的类型是什么。
- 而且union无法自动调用底层数据成员的析构函数。
这些使得一般只对一些“基本类型”使用union,无法在union里放std::string或者是对象之类的。举个例子:
union CannotUnion
{string vec;
};
这样一个 union 结构,然后在 main 函数里这么写:
CannotUnion specU = {"I CANNOT"};cout << specU.vec << endl;
甚至于在初始化 specU 的时候,IDE就会发出警告
如果你尝试编译,就会产生如下报错信息:
UnionTest.cpp: In function 'int main()':
UnionTest.cpp:26:36: error: use of deleted function 'CannotUnion::~CannotUnion()'26 | CannotUnion specU = {"I CANNOT"};| ^
UnionTest.cpp:13:7: note: 'CannotUnion::~CannotUnion()' is implicitly deleted because the default definition would be ill-formed:13 | union CannotUnion| ^~~~~~~~~~~
UnionTest.cpp:15:12: error: union member 'CannotUnion::vec' with non-trivial 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::~basic_string() [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'15 | string vec;| ^~~
C++11以前,union只允许有一些“基本类型”,也就是与C语言相兼容的类型(POD,Plain Old Data)。这些类型可以以二进制的方式转换为C语言的类型。C++11以后,如果非受限联合体内有一个非 POD 的成员,而该成员拥有自定义的构造函数,那么这个非受限联合体的默认构造函数将被编译器删除;其他的特殊成员函数,例如默认拷贝构造函数、拷贝赋值操作符以及析构函数等,也将被删除。
所以想让上面的代码正常运行,把被删除的构造和析构函数还原回来就行了:
union CannotUnion
{string vec;CannotUnion(string vec):vec(vec){};~CannotUnion(){};
};
variant的用法
我一般会提前一段时间接触我马上要写的语法知识。但是我有时候在想,这么复杂的结构真的有必要吗。
variant的用法如下:
#include<iostream>
#include<variant>
#include<string>using namespace std;int main()
{variant<int, string, float> myVar;myVar = "Hello variant";
}
访问
union访问的时候,由于每个成员变量都有自己的变量名,因此直接就可以访问。但是variant不太行,而且还要更麻烦一点。
最简单的就是用get
cout << get<string>(myVar) << endl;
但是这里存在一个问题,如果类型对了那皆大欢喜;类型错了,还要处理抛出的std::bad_variant_access
异常:
我们可以使用get_if,在判断类型再进行访问。get_if判断类型成功会返回指向数据的指针,判断失败会返回空指针。
if(auto ptr = get_if<string>(&myVar)){cout << *ptr << endl;}
visit
这个的机制有点像设计模式里的访问者模式(完了我设计模式还没写)
visit([](auto args){ cout << args << endl; },myVar);
第一个参数是一个函数,函数的参数可以用auto args
,后面输出即可。
如果需要针对多个类型的元素进行判断,可以使用类似于访问者模式的写法
以下代码来自:https://blog.csdn.net/qq_21438461/article/details/132659408
// 定义 variant 类型
using MyVariant = std::variant<int, double, std::string>;// 访问者函数对象
struct VariantVisitor {void operator()(int i) const {std::cout << "处理 int: " << i << std::endl;}void operator()(double d) const {std::cout << "处理 double: " << d << std::endl;}void operator()(const std::string& s) const {std::cout << "处理 string: " << s << std::endl;}
};int main() {MyVariant v1 = 10; // v1 存储 intMyVariant v2 = 3.14; // v2 存储 doubleMyVariant v3 = "hello"; // v3 存储 stringstd::visit(VariantVisitor(), v1); // 输出: 处理 int: 10std::visit(VariantVisitor(), v2); // 输出: 处理 double: 3.14std::visit(VariantVisitor(), v3); // 输出: 处理 string: helloreturn 0;
}