「C/C++」C++17 之 std::variant 安全的联合体(变体)

news/2024/10/25 15:09:44/

#1024程序员节|征文#
在这里插入图片描述
在这里插入图片描述

✨博客主页
何曾参静谧的博客
📌文章专栏
「C/C++」C/C++程序设计
📚全部专栏
「VS」Visual Studio「C/C++」C/C++程序设计「UG/NX」BlockUI集合
「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发
「QT」QT5程序设计「File」数据文件格式「PK」Parasolid函数说明

目录

    • std::variant详解
      • 1. 引用头文件
      • 2. 注意事项
      • 3. 函数构造
      • 4. 对象初始化
      • 5. 元素访问
      • 6. 迭代器(不适用)
      • 7. 容器(不适用)
      • 8. 修改器
      • 9. 元素比较
      • 10.其他用法
        • 总结
      • 应用场景

std::variant详解

在这里插入图片描述
在这里插入图片描述

1. 引用头文件

std::variant是C++17引入的一种类型安全的联合体,用于存储多个可能类型中的一种值。要使用std::variant,首先需要包含头文件#include <variant>

#include <variant>

2. 注意事项

  • std::variant不允许存储引用、数组或void类型。
  • std::variant没有空的状态,每个构造好的std::variant对象至少调用了一次构造函数。
  • 如果std::variant的第一个类型没有默认构造函数,那么调用std::variant的默认构造函数会导致编译错误。此时,可以使用std::monostate作为第一个类型来确保std::variant能默认构造。
  • 使用std::variant时,如果存储的类型有可能在语义上重叠或不明确(如std::variant<int, float>),需要特别注意操作和类型检查的准确性。

3. 函数构造

std::variant的构造函数允许通过直接赋值、std::in_place_type或std::in_place_index等方式进行初始化。

#include <iostream>
#include <variant>
#include <string>int main() {std::variant<int, double, std::string> v1 = 42; // 直接赋值std::variant<std::string, int> v2{std::in_place_type<std::string>, "Hello"}; // 使用std::in_place_typestd::variant<std::string, int, float> v3{std::in_place_index<1>, 66}; // 使用std::in_place_indexstd::cout << v1.index() << std::endl; //当前属于的类型 输出: 0std::cout << std::get<int>(v1) << std::endl; // 输出: 42std::cout << std::get<std::string>(v2) << std::endl; // 输出: Hellostd::cout << std::get<int>(v3) << std::endl; // 输出: 66return 0;
}

4. 对象初始化

std::variant对象可以通过直接赋值、构造函数初始化列表等方式进行初始化。

std::variant<int, double, std::string> v = 3.14; // 直接赋值初始化
std::variant<std::string, int> w{"World"}; // 使用构造函数初始化列表

5. 元素访问

访问std::variant中的元素可以使用std::get(variant)或std::get(variant),也可以使用std::visit进行更通用的访问。

#include <iostream>
#include <variant>
#include <string>int main() {std::variant<int, double, std::string> v = "Hello";// 使用std::get访问try {std::cout << std::get<int>(v) << std::endl; // 这将抛出异常} catch (const std::bad_variant_access& e) {std::cout << "Error: " << e.what() << std::endl; // 输出: Error: bad variant access}// 使用std::get_if访问std::cout << std::get_if<int>(&v) << std::endl; // 输出: nullptr 0000000000000000std::cout << *(std::get_if<std::string>(&v)) << std::endl; // 输出: hello// 使用std::visit访问std::visit([](auto&& arg) { std::cout << arg << std::endl; }, v); // 输出: Helloreturn 0;
}

6. 迭代器(不适用)

std::variant不支持迭代器,因为它不是容器类型,而是存储单一值的类型安全的联合体。

7. 容器(不适用)

虽然std::variant不能作为容器使用,但它可以存储容器类型,如std::vector<int>等。

std::variant<std::vector<int>, std::string> container = std::vector<int>{1, 2, 3};

8. 修改器

std::variant的值可以通过重新赋值进行修改。

std::variant<int, double, std::string> v = 42;
v = 3.14; // 修改为double类型
v = "Hello"; // 修改为std::string类型

9. 元素比较

std::variant不支持直接比较,但可以通过std::visit和适当的比较逻辑来实现比较功能。

#include <iostream>
#include <variant>
#include <string>bool compare_variants(const std::variant<int, double, std::string>& a, const std::variant<int, double, std::string>& b) {return std::visit([](auto&& arg1, auto&& arg2) { return arg1 == arg2; }, a, b);
}int main() {std::variant<int, double, std::string> v1 = 42;std::variant<int, double, std::string> v2 = "42"; // 注意这里是字符串类型// 比较会失败,因为类型不同if (!compare_variants(v1, v2)) {std::cout << "v1 and v2 are not equal." << std::endl; // 输出: v1 and v2 are not equal.}return 0;
}

10.其他用法

    using var = std::variant<int, double, std::string>;var value = 1;int b = 0;b = std::get<int>(value);
总结

std::variant是C++17引入的一种类型安全的联合体,用于存储多个可能类型中的一种值。它提供了类型安全和便利的接口,相比传统的联合体(union)具有更高的安全性和灵活性。std::variant支持多种类型的存储和访问,可以通过std::get、std::visit等方式进行元素访问,同时提供了类型检查和异常处理机制。

应用场景

  1. 配置选项:在开发中,配置项可能需要支持多种数据类型(如整数、字符串、布尔值等)。使用std::variant可以简化配置管理,使得一个配置变量能够存储多种类型的配置值。
  2. 解析器:在编写如JSON解析器或其他形式的解析器时,数据结构可能需要存储不同类型的数据。std::variant提供了一种安全、灵活的方式来存储解析后的数据,从而简化代码并增强其健壮性。
  3. 状态机:在实现状态机时,每个状态可能需要不同类型的数据来描述。std::variant可以用来存储状态相关的数据,使得状态转换和数据处理更加灵活和安全。
  4. 命令模式:在实现命令模式时,如果命令的参数类型多样,std::variant可以作为一个通用的参数容器,提供统一的接口而隐藏实现细节。

通过了解和利用std::variant的这些特点和高级用法,可以在C++中更有效地处理那些需要存储和操作多种数据类型的场景,同时保持代码的整洁性和安全性。


在这里插入图片描述


http://www.ppmy.cn/news/1541854.html

相关文章

串口通讯编程示例之串口完整代码

注意&#xff1a;学习资料可在ElfBoard官方网站“资料”专区获取。 本节用到的源码路径&#xff1a;ELF 1开发板资料包->03-例程源码->03-1 命令行例程源码->02_elf1_cmd_serialport serial.c 本节代码提供了串口通信的函数库&#xff0c;主要分三部分&#xff1a; …

基于NVIDIA NIM平台—生成属于自己的DIY食谱

目录 一、介绍NVIDIA NIM平台 二、生成DIY食谱Demo 三、小结 一、介绍NVIDIA NIM平台 NVIDIA NIM&#xff08;Nvidia Inference Microservices&#xff09;平台是NVIDIA推出的一个微服务套件&#xff0c;旨在加速生成式AI模型在云端、数据中心和工作站上的部署和使用。以下是…

证明在由特定矩阵生成的幺半子群中,存在收敛序列的子序列,其元素也能分别构成收敛序列

设 H H H是 G L 4 ( R ) GL_4(\mathbb{R}) GL4​(R)的由矩阵 ( 1 a 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ) , ( 1 0 0 0 0 1 b 0 0 0 1 0 0 0 0 1 ) , ( 1 0 0 0 0 1 0 0 0 0 1 c 0 0 0 1 ) \begin{pmatrix}1&a&0&0\\ 0&1&0&0\\ 0&0&1&0\\ 0&…

【纯血鸿蒙】安装hdc工具

这里我先写Mac版的,Windows的在下面 首先要知道你的SDK安装在哪里了,不知道的话,可以打开DevEco Studio,打开设置页面里的HarmonyOS SDK,这个我们之前配置环境变量的时候用过。 其实主要是用到这里toolchains下的hdc命令。 所以我们需要配置环境变量。 1、打开Mac下的…

ElasticSearch全文检索和倒排索引

本文内容参考了田雪松老师编著的《Elastic Stack应用宝典》 全文检索 先来解释一下什么叫全文检索。数据检索的目的是从一系列数据中&#xff0c;根据某一或某些数据特征将特定的数据找出来。从数据检索的角度来看&#xff0c;数据大体上可以分为两种类型&#xff1a;一种是结…

Java基础第一天(实训学习整理资料(一))

目录 一、Typora--markdown编辑器 二、dos常用指令 操作系统 相关的dos指令 三、Java语言简介 发展历史 版本变迁 3、java的技术结构 4、java的跨平台特性 四、Java的下载和安装 1、下载地址 2、文件说明 3、安装 五、第一个Java程序HelloWorld 六、Java保留字&a…

命名空间std, using namespace std

命名空间std&#xff0c;using namespace std 在标准C以前&#xff0c;都是用#include<iostream.h>这样的写法的&#xff0c;因为要包含进来的头文件名就是iostream.h。标准C引入了名字空间的概念&#xff0c;并把iostream等标准库中的东东封装到了std名字空间中&#x…

【前端】JQ读取本地txt文本内容,并显示到页面上

目录 需求&#xff1a; 效果图 示例代码 需求&#xff1a; 有一个本地txt文件&#xff0c;把txt文件的内容按照原格式显示到页面上。 有两种解决方案&#xff0c;但是只支持UTF8编码的txt文件&#xff0c;如果是其他编码请转换成UTF8&#xff0c;这里有一个批量转换工具可以…