自学内容网 自学内容网

【C++】std::variant

上一篇文章讲到了 union,union

union存在很多问题,因此C++17设计了一个新的variant替代原来的union。

union的问题

  1. 无法知道当前使用的类型是什么。
  2. 而且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 存储 int
    MyVariant v2 = 3.14;      // v2 存储 double
    MyVariant v3 = "hello";   // v3 存储 string

    std::visit(VariantVisitor(), v1); // 输出: 处理 int: 10
    std::visit(VariantVisitor(), v2); // 输出: 处理 double: 3.14
    std::visit(VariantVisitor(), v3); // 输出: 处理 string: hello

    return 0;
}

原文地址:https://blog.csdn.net/qq_37387199/article/details/135883575

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!