C++17 std::variant 详解:概念、用法和实现细节

image.png

文章目录

    • 简介
    • 基本概念
      • 定义和使用std::variant
      • 与传统联合体union的区别
    • 多类型值存储示例
      • 初始化
      • 修改
      • 判断variant中对应类型是否有值
      • 获取std::variant中的值
      • 获取当前使用的type在variant声明中的索引
    • 访问std::variant中的值
      • 使用std::get
      • 使用std::get_if
    • 错误处理和访问未初始化的std::variant
    • 应用场景
      • 解析命令行
      • 解析ini文件
      • 语言解析器
      • 求解方程的根
      • 错误处理
      • 状态机
      • 不使用虚表和继承实现的多态
    • 总结

简介

在C++的发展历程中,C++17带来了许多实用的新特性,其中std::variant尤为引人注目。它本质上是一种类型安全的联合体,能够在同一时刻持有多种可能类型中的某一个值。这种特性为开发者提供了极大的便利,在面对需要处理多种不同类型数据的场景时,std::variant提供了一种灵活且高效的解决方案,使得代码编写更加简洁、安全。

基本概念

定义和使用std::variant

std::variant是一个模板类,借助模板参数包的特性,它能够存储多种不同类型的值。其声明形式如下:

template<class... Types>
class variant;

这里的Types代表了一系列的类型,意味着我们可以根据实际需求,传入任意数量和种类的类型。例如,若要创建一个std::variant对象,使其能够存储intstd::stringdouble类型的值,可以这样定义:

std::variant<int, std::string, double> myVar;

与传统联合体union的区别

传统的C风格联合体union虽然也能实现存储不同类型的值,但与std::variant相比,存在诸多劣势。首先,std::variant具备类型安全性,而union则需要开发者手动管理数据成员的活跃性。在使用union时,如果错误地访问了当前未存储的类型数据,就会导致未定义行为。而std::variant会自动跟踪当前存储的值的类型,开发者无需手动干预。其次,std::variant提供了更为友好和安全的访问方式,使得代码在处理不同类型数据时更加可靠和易于理解。

多类型值存储示例

初始化

std::variant对象的初始化十分便捷。以下面代码为例,创建一个std::variant对象v,并初始化为int类型的值123:

#include <iostream>
#include <variant>int main() {std::variant<int, std::string, double> v(123);return 0;
}

修改

在程序运行过程中,可以根据实际需求修改std::variant对象所存储的值的类型。例如,将上述v的值修改为std::string类型的"HelloWorld":

#include <iostream>
#include <variant>int main() {std::variant<int, std::string, double> v(123);v = "HelloWorld";return 0;
}

判断variant中对应类型是否有值

为了确保类型安全,经常需要判断std::variant中是否存储了特定类型的值。这时,可以使用std::holds_alternative函数来实现:

#include <iostream>
#include <variant>int main() {std::variant<int, std::string, double> v(123);v = "HelloWorld";if (std::holds_alternative<std::string>(v)) {std::cout << "has std::string" << std::endl;}return 0;
}

获取std::variant中的值

获取std::variant中的值主要有两种方式。一种是通过指定类型来获取:

#include <iostream>
#include <variant>int main() {std::variant<int, std::string, double> v("HelloWorld");std::cout << std::get<std::string>(v) << std::endl;return 0;
}

另一种是通过索引来获取,索引从0开始计数:

#include <iostream>
#include <variant>int main() {std::variant<int, std::string, double> v("HelloWorld");std::cout << std::get<1>(v) << std::endl;return 0;
}

获取当前使用的type在variant声明中的索引

通过调用index成员函数,可以获取当前std::variant中存储的值的类型在声明时的索引位置:

#include <iostream>
#include <variant>int main() {std::variant<int, std::string, double> v("HelloWorld");std::cout << v.index() << std::endl;return 0;
}

访问std::variant中的值

使用std::get

std::get是访问std::variant中值的常用方法,如前文示例,它既可以通过指定类型,也能通过索引来获取值。不过,使用时需注意,如果std::variant中当前存储的值并非所指定的类型,会抛出std::bad_variant_access异常。

使用std::get_if

std::get_if是另一种访问std::variant值的方式,它能避免抛出异常。当std::variant中存储的是指定类型的值时,std::get_if会返回一个指向该值的指针;否则,返回nullptr。示例如下:

#include <iostream>
#include <variant>int main() {std::variant<int, std::string, double> v("HelloWorld");if (auto str = std::get_if<std::string>(&v)) {std::cout << *str << std::endl;}return 0;
}

错误处理和访问未初始化的std::variant

std::variant未进行初始化,或者当前存储的值并非期望获取的类型时,调用std::get会抛出std::bad_variant_access异常。例如:

#include <iostream>
#include <variant>int main() {std::variant<int, std::string, double> v(123);try {std::cout << std::get<std::string>(v) << std::endl;} catch (const std::bad_variant_access& e) {std::cerr << "Caught exception: " << e.what() << std::endl;}return 0;
}

而使用std::get_if可以避免这种异常情况的发生,通过检查返回的指针是否为nullptr,来决定是否进行后续操作。

应用场景

解析命令行

在解析命令行参数时,参数可能有多种类型,如整数、字符串等。std::variant可以方便地存储和处理这些不同类型的参数。

解析ini文件

ini文件中的配置项可能有不同的数据类型,std::variant能有效地处理这种多类型数据的解析。

语言解析器

语言解析过程中,词法单元可能有多种类型,如标识符、关键字、常量等。std::variant可以用来存储和管理这些不同类型的词法单元。

求解方程的根

在数值计算中,方程的根可能是实数、复数等不同类型,std::variant可以灵活地存储这些结果。

错误处理

在函数返回值中,可以使用std::variant来同时表示成功结果和错误信息,通过不同的类型来区分。

状态机

状态机的状态可能有多种类型,std::variant可以用于存储和管理这些状态。

不使用虚表和继承实现的多态

通过std::variant结合std::visit(本文未详细介绍),可以实现一种不依赖虚表和继承的多态机制。

总结

std::variant作为C++17的重要特性之一,为开发者提供了强大的功能。它以类型安全和便捷的接口,使得处理多种可能类型的数据变得轻松且安全。在实际编程中,合理运用std::variant,能够显著增强代码的灵活性和可维护性,让代码更加简洁高效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/69431.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NLP自然语言处理通识

目录 ELMO 一、ELMo的核心设计理念 1. 静态词向量的局限性 2. 动态上下文嵌入的核心思想 3. 层次化特征提取 1. 双向语言模型&#xff08;BiLM&#xff09; 2. 多层LSTM的层次化表示 三、ELMo的运行过程 1. 预训练阶段 2. 下游任务微调 四、ELMo的突破与局限性 1. 技术突破 2. …

在做题中学习(82):最小覆盖子串

解法&#xff1a;同向双指针——>滑动窗口 思路&#xff1a;题目要求找到s里包含t所有字符的最小子串&#xff0c;这就需要记录在s中每次查找并扩大范围时所包含进去的字符种类是否和t的相同&#xff0c;并且&#xff1a;题目提示t中会有重复字符&#xff0c;因此不能简单认…

【deepseek】deepseek-r1本地部署-第二步:huggingface.co替换为hf-mirror.com国内镜像

一、背景 由于国际镜像国内无法直接访问&#xff0c;会导致搜索模型时加载失败&#xff0c;如下&#xff1a; 因此需将国际地址替换为国内镜像地址。 二、操作 1、使用vscode打开下载路径 2、全局地址替换 关键字 huggingface.co 替换为 hf-mirror.com 注意&#xff1a;务…

DeepSeek:突破传统的AI算法与下载排行分析

DeepSeek的AI算法突破DeepSeek相较于OpenAI以及其它平台的性能对比DeepSeek的下载排行分析&#xff08;截止2025/1/28 AI人工智能相关DeepSeek甚至一度被推上了搜索&#xff09;未来发展趋势总结 在人工智能技术飞速发展的当下&#xff0c;搜索引擎市场也迎来了新的变革。DeepS…

java 判断Date是上午还是下午

我要用Java生成表格统计信息&#xff0c;如下图所示&#xff1a; 所以就诞生了本文的内容。 在 Java 里&#xff0c;判断 Date 对象代表的时间是上午还是下午有多种方式&#xff0c;下面为你详细介绍不同的实现方法。 方式一&#xff1a;使用 java.util.Calendar Calendar 类…

【Matlab高端绘图SCI绘图模板】第05期 绘制高阶折线图

1.折线图简介 折线图是一个由点和线组成的统计图表&#xff0c;常用来表示数值随连续时间间隔或有序类别的变化。在折线图中&#xff0c;x 轴通常用作连续时间间隔或有序类别&#xff08;比如阶段1&#xff0c;阶段2&#xff0c;阶段3&#xff09;。y 轴用于量化的数据&#x…

【Java数据结构】了解排序相关算法

基数排序 基数排序是桶排序的扩展&#xff0c;本质是将整数按位切割成不同的数字&#xff0c;然后按每个位数分别比较最后比一位较下来的顺序就是所有数的大小顺序。 先对数组中每个数的个位比大小排序然后按照队列先进先出的顺序分别拿出数据再将拿出的数据分别对十位百位千位…

Linux的常用指令的用法

目录 Linux下基本指令 whoami ls指令&#xff1a; 文件&#xff1a; touch clear pwd cd mkdir rmdir指令 && rm 指令 man指令 cp mv cat more less head tail 管道和重定向 1. 重定向&#xff08;Redirection&#xff09; 2. 管道&#xff08;Pipes&a…

Ubuntu20.04 磁盘空间扩展教程

Ubuntu20.04 磁盘空间扩展教程_ubuntu20 gpart扩容-CSDN博客文章浏览阅读2w次&#xff0c;点赞38次&#xff0c;收藏119次。执行命令查看系统容量相关的数据&#xff1a;df -h当前容量为20G&#xff0c;已用18G&#xff08;96%&#xff09;&#xff0c;可用844M&#xff0c;可用…

使用Maxscript定义纹理贴图的方法

在3ds Max中,MaxScript 是一种用于插件编写和自动化任务的强大工具。通过MaxScript,你可以创建和操作对象、材质、灯光等等。要为材质分配纹理贴图,你可以按照以下方法来编写脚本。直接代码: myBmp = bitmaptexture filename:"D:\map001.tga" meditmaterials[1]…

每日一题-判断是否是平衡二叉树

判断是否是平衡二叉树 题目描述数据范围题解解题思路递归算法代码实现代码解析时间和空间复杂度分析示例示例 1示例 2 总结 ) 题目描述 输入一棵节点数为 n 的二叉树&#xff0c;判断该二叉树是否是平衡二叉树。平衡二叉树定义为&#xff1a; 它是一棵空树。或者它的左右子树…

21款炫酷烟花合集

系列专栏 《Python趣味编程》《C/C趣味编程》《HTML趣味编程》《Java趣味编程》 写在前面 Python、C/C、HTML、Java等4种语言实现18款炫酷烟花的代码。 Python Python烟花① 完整代码&#xff1a;Python动漫烟花&#xff08;完整代码&#xff09; ​ Python烟花② 完整…

2025美赛美国大学生数学建模竞赛A题完整思路分析论文(43页)(含模型、可运行代码和运行结果)

2025美国大学生数学建模竞赛A题完整思路分析论文 目录 摘要 一、问题重述 二、 问题分析 三、模型假设 四、 模型建立与求解 4.1问题1 4.1.1问题1思路分析 4.1.2问题1模型建立 4.1.3问题1样例代码&#xff08;仅供参考&#xff09; 4.1.4问题1样例代码运行结果&…

day6手机摄影社区,可以去苹果摄影社区学习拍摄技巧

逛自己手机的社区&#xff1a;即&#xff08;手机牌子&#xff09;摄影社区 拍照时防止抖动可以控制自己的呼吸&#xff0c;不要大喘气 拍一张照片后&#xff0c;如何简单的用手机修图&#xff1f; HDR模式就是让高光部分和阴影部分更协调&#xff08;拍风紧时可以打开&…

【翻转硬币——莫比乌斯函数、分块、卷积、埃氏筛】

题目 暴力代码&#xff0c;官网过55% #include <bits/stdc.h> using namespace std; int main() {int n;cin >> n;vector<bool> a(n 1);a[1] 1;int res 1;for (int i 2; i < n; i){if (a[i] 0){for (int j i; j < n; j i)a[j] a[j] ^ 1;res;}…

2025.1.26机器学习笔记:C-RNN-GAN文献阅读

2025.1.26周报 文献阅读题目信息摘要Abstract创新点网络架构实验结论缺点以及后续展望 总结 文献阅读 题目信息 题目&#xff1a; C-RNN-GAN: Continuous recurrent neural networks with adversarial training会议期刊&#xff1a; NIPS作者&#xff1a; Olof Mogren发表时间…

VMware 中Ubuntu无网络连接/无网络标识解决方法【已解决】

参考文档 Ubuntu无网络连接/无网络标识解决方法_ubuntu没网-CSDN博客 再我们正常使用VMware时&#xff0c;就以Ubuntu举例可能有时候出现无网络连接&#xff0c;甚至出现无网络标识的情况&#xff0c;那么废话不多说直接上教程 环境&#xff1a;无网络 解决方案&#…

win11系统,Java web程序连不上数据的的解决办法

买了台新笔记本电脑&#xff0c;把代码和数据考了过来&#xff0c;想着能愉快的写代码了&#xff0c;程序起来发现连不上数据库。 所有的配置翻了一遍&#xff0c;也没发现问题&#xff0c;遂怀疑是系统的问题&#xff0c;原电脑是win10,现电脑是win11&#xff0c;所以晚上冲浪…

人工智能学习框架:深入解析与实战指南

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;深度学习、强化学习和自然语言处理等领域的应用愈加广…

The Simulation技术浅析(二):模型技术

一、物理模型(Physical Models) 1. 概述 物理模型基于物理定律和原理,通过模拟现实世界中物理系统的行为和相互作用来构建模型。物理模型通常用于工程、物理和化学等领域,用于预测系统在不同条件下的表现。 2. 关键技术 力学定律:例如牛顿运动定律,用于模拟物体的运动…