前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >类型安全的瑞士军刀——std::variant

类型安全的瑞士军刀——std::variant

作者头像
程序员的园
发布2024-07-18 13:22:07
860
发布2024-07-18 13:22:07
举报
文章被收录于专栏:程序员的园——原创文章

前言

当需要在同一块内存区域中存储不同类型的值且在任何时刻只会存储其中的一种类型时,联合体(union)总是作为首要选择,但是联合体存在如类型安全差、不支持构造函数和析构函数等缺点。为避免union存在的问题,C++17引入一个非常实用且强大的新特性——std::variant。

std::variant作为一个多形态的容器,可以容纳一组预定义类型的其中之一,任何时候它都只存储其中一个类型的有效值,提供了严格的类型安全保证。

联合体通过.指定变量名进行变量存取,如下示例代码1。而std::variant型变量可以使用std::get<T>()和std::visit函数读取变量值

代码语言:javascript
复制
//示例代码1

union MyUnion

{

    int i;

    float f;

    double d;

};



MyUnion u;
u.d =1.0;

写 std::variant变量

1. std::variant在未初始化时,默认调用第一个类型变量的默认构造函数,并将该值作为std::variant型变量的初值。如下示例验证

代码语言:javascript
复制
struct  Point

{

    float x;

    float y;

    float z;

    friend std::ostream& operator<<(std::ostream& os,const Point& p)

    {

           return os<<p.x<<" "<<p.y<<" "<<p.z;

    }

};



int using_variant() {

    std::variant<Point,int, std::string, double> multiTypeVar;

    //函数对象

    std::visit([](auto val) {

        std::cout << "Value of type " << typeid(val).name() << ": " << val << std::endl;

        } , multiTypeVar);

    return 0;

}

//输出

//Value of type struct Point: 0 0 0

2. std::variant型变量赋值

可以使用emplace函数或=对std::variant型变量赋值

代码语言:javascript
复制
int using_variant() {
    std::variant<int, std::string, double> multiTypeVar;
    multiTypeVar.emplace<double>(0.8);
    std::visit([](auto val) {
        std::cout << "Value of type " << typeid(val).name() << ": " << val << std::endl;
        }, multiTypeVar);


    multiTypeVar=10;
    std::visit([](auto val) {
        std::cout << "Value of type " << typeid(val).name() << ": " << val << std::endl;
        } , multiTypeVar);
    return 0;
}

读——使用std::get<T>

代码示例如下

代码语言:javascript
复制
#include <variant>
#include <iostream>


int main() {
    std::variant<int, std::string> myVariant;

    myVariant = 42;
    std::cout << "初始值:" << std::get<int>(myVariant) << std::endl; // 输出"初始值:42"

    myVariant = "Hello, World!";
    std::cout << "字符串值:" << std::get<std::string>(myVariant) << std::endl; // 输出"字符串值:Hello, World!"


    // 通过index()函数获取当前存储值的类型索引
    if (myVariant.index() == 0) {
        std::cout << "当前存储的是int类型" << std::endl;
    } else if (myVariant.index() == 1) {
        std::cout << "当前存储的是std::string类型" << std::endl;
    }
   
    return 0;
}

上述代码展示了如何创建一个能存储int和std::string类型的std::variant,并根据需要在两者之间切换。值得注意的是,直接通过std::get<T>(myVariant)访问值时,必须确保当前存储的类型与T一致,否则会抛出std::bad_variant_access异常。

读——使用std::visit函数

std::visit函数为了更加安全地处理std::variant中的值,它接受一个可调用体(callable,函数对象/lambda表达式/std::function)和一个std::variant实例,根据variant中实际存储的类型调用访问者的相应重载方法。下面是一个使用std::visit的例子

代码语言:javascript
复制
#include<variant>
#include<string>


struct VariantPrinter {
    template <typename T>
    void operator()(T&& value) const {
        std::cout << "Value of type " << typeid(T).name() << ": " << value << std::endl;
    }
};


int using_variant() {
    std::variant<int, std::string, double> multiTypeVar = 3.14;
    //函数对象
    std::visit(VariantPrinter{}, multiTypeVar); // 根据实际类型输出


    multiTypeVar = 10;
    //lambda表达式
    std::visit([](auto val){
        std::cout << "Value of type " << typeid(val).name() << ": " << val << std::endl;
        },
        multiTypeVar);


    return 0;
}

总结

std::variant以其类型安全性、内存高效性以及强大的多态处理能力,极大地丰富了C++程序设计的手段。熟练掌握这一特性,将有助于我们编写更为健壮、高效的代码。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档