std::variant 是 C++17 引入的一种类型安全的联合体,用来存储多个可能类型中的一种值,且保证使用时的类型安全。相比于传统的 unionstd::variant 不仅能够存储不同类型的值,还能自动管理复杂类型的构造与析构。

如何快速上手 std::variant

1. 定义 std::variant

使用 std::variant 可以定义一个变量,该变量可以持有多种不同类型的值,但一次只能存储一种。

#include <iostream>
#include <variant> int main() {
std::variant<int, float, std::string> v; // v 可以是 int、float 或 std::string // 设置为 int 类型
v = 42;
std::cout << std::get<int>(v) << std::endl; // 设置为 float 类型
v = 3.14f;
std::cout << std::get<float>(v) << std::endl; // 设置为 std::string 类型
v = "Hello, std::variant!";
std::cout << std::get<std::string>(v) << std::endl; return 0;
}

2. 访问 std::variant 的值

std::variant 的值可以通过 std::get<type>std::get<index> 访问。

std::variant<int, float, std::string> v = 42;

// 使用类型访问
std::cout << std::get<int>(v) << std::endl; // 使用索引访问,0 表示第一个类型
std::cout << std::get<0>(v) << std::endl;

注意:

  • 如果你尝试访问的类型与当前存储的类型不匹配,程序会抛出 std::bad_variant_access 异常。
std::variant<int, float, std::string> v = 42;
try {
std::cout << std::get<float>(v); // 当前不是 float 类型,会抛出异常
} catch (const std::bad_variant_access& e) {
std::cout << "Wrong type access: " << e.what() << std::endl;
}

3. 检查当前类型

你可以使用 std::holds_alternative<T>(variant) 来判断 std::variant 当前是否持有某种类型。

if (std::holds_alternative<int>(v)) {
std::cout << "v holds an int" << std::endl;
}

4. 访问当前存储类型的索引

你可以使用 v.index() 获取当前存储值的类型在 std::variant 中的索引。

std::cout << "Index: " << v.index() << std::endl;  // 0 表示 int,1 表示 float,依次类推

5. 使用 std::visit 访问 std::variant

std::visitstd::variant 的一个访问工具,它使用一个可调用对象(如 lambda 表达式或函数)来访问 std::variant 的值,而无需手动判断当前的类型。

#include <iostream>
#include <variant>
#include <string> int main() {
std::variant<int, float, std::string> v = "Hello"; std::visit([](auto&& arg) {
std::cout << arg << std::endl; // 打印不同类型的值
}, v); return 0;
}

6. 常见应用场景

  • 存储多种类型的值:当需要一个变量存储多种可能的类型时,std::variantunion 更灵活和安全。
  • 事件系统:可以使用 std::variant 来构建一种通用的事件系统,不同事件类型对应不同的 variant
  • 解析数据:如解析 JSON 或 XML 等格式化数据,数据字段可能是不同的类型。

小结

  • std::variant 可以存储多个类型之一,并且类型安全。
  • 通过 std::get<type>std::get<index> 访问值。
  • 使用 std::holds_alternative 判断存储类型,使用 std::visit 处理不同类型的值。

    std::monostate 是 C++17 引入的一种特殊类型,通常用于与 std::variant 一起使用。std::monostate 本身没有任何成员或功能,主要作用是用作默认的、无效的状态。当 std::variant 没有匹配到任何实际类型时,可以使用 std::monostate 作为占位符。

    std::variant 是 C++17 引入的一个类型,用于表示一个值可以是多个类型中的任意一种。当您将 std::variant 作为函数参数时,它可以接受构造函数中定义的任意一种类型的输入。

std::variant函数参数

首先,让我们定义一个 std::variant,例如:

#include <variant>
#include <string>
#include <iostream> using MyVariant = std::variant<int, double, std::string>;

在这个例子中,MyVariant 可以接受三种类型的值:intdoublestd::string

函数参数

您可以将 std::variant 作为函数参数,接受这些类型的任意一种。例如:

void processVariant(const MyVariant& value) {
std::visit([](auto&& arg) {
std::cout << "Processing: " << arg << std::endl;
}, value);
}

在这个函数中,使用 std::visit 可以访问 variant 中存储的值,并根据实际类型执行相应的操作。

调用示例

您可以通过多种方式调用这个函数,传入不同类型的值:

int main() {
MyVariant v1 = 42; // int
MyVariant v2 = 3.14; // double
MyVariant v3 = std::string{"Hello"}; // std::string processVariant(v1); // 输出: Processing: 42
processVariant(v2); // 输出: Processing: 3.14
processVariant(v3); // 输出: Processing: Hello return 0;
}

关键点

  1. 多态性std::variant 提供了一种类型安全的方式来处理多种类型,可以用在函数参数中以接受这些类型。

  2. 类型安全:使用 std::visit 可确保访问的值是有效的,避免了类型错误。

  3. 构造和赋值:您可以直接使用各种支持的类型初始化 std::variant,并在需要时将其传递给函数。

  • std::variant 可以作为函数参数,接受多种类型的值,这些类型是在定义 std::variant 时指定的。
  • 通过 std::visit,您可以安全地处理存储在 std::variant 中的不同类型的值。
  • 这种机制为处理异构数据提供了灵活性和安全性。

std::monostate使用场景:

  1. 作为 std::variant 的默认类型: std::variant 是一个可以保存多个不同类型的值的容器。std::monostate 可以作为 std::variant 的第一个类型,以处理变体未初始化的情况。

  2. 处理空状态: 通过将 std::monostate 添加到 std::variant 中的类型列表,可以让 std::variant 有一个明确的“空”状态。

#include <iostream>
#include <variant> int main() {
// 定义一个 std::variant,可以保存 int、double 或 std::monostate
std::variant<std::monostate, int, double> var; // 初始时,variant 包含的是 std::monostate
if (std::holds_alternative<std::monostate>(var)) {
std::cout << "Variant is in the default state (monostate)\n";
} // 赋值一个 int 类型
var = 42; if (std::holds_alternative<int>(var)) {
std::cout << "Variant holds an int: " << std::get<int>(var) << "\n";
} return 0;
}
Variant is in the default state (monostate)
Variant holds an int: 42

std::monostate 的特点:

  1. 类型安全:它是一个有类型的空状态,并且可以在 std::variant 中使用时避免无效状态。
  2. 支持比较运算std::monostate 支持相等比较(==)和小于比较(<),因此可以用于比较 std::variant 中的状态。
  3. 默认无效状态:当 std::variant 初始化时,没有赋值任何有效类型,std::monostate 可以表示这种状态。

    std::get_if<> 是 C++17 引入的一个函数,通常用于与 std::variant 一起使用。它的作用是安全地访问 std::variant 中存储的某种类型的值,并返回指向该类型值的指针。如果 std::variant 中存储的不是该类型,则返回 nullptr。这个函数避免了直接使用 std::get<> 可能导致的异常抛出。

作用和使用场景:

  • 类型安全的访问std::get_if<> 可以在访问 std::variant 时避免异常,通过返回指针的方式来安全判断实际存储的类型。
  • 避免抛异常std::get<> 在类型不匹配时会抛出 std::bad_variant_access 异常,而 std::get_if<> 返回 nullptr,因此可以在不使用异常处理的情况下检测类型。

std::get_if<>

主要有两种形式:

  1. 对于 std::variant 非常量对象的访问

    T* std::get_if<T>(&variant);

    这将返回一个指向 T 类型的指针,如果 variant 中存储的不是 T 类型,则返回 nullptr

  2. 对于 std::variant 常量对象的访问

    const T* std::get_if<T>(const &variant);

    如果是常量对象,返回的是 const T* 指针。否则返回 nullptr

示例

#include <iostream>
#include <variant> int main() {
std::variant<int, double, std::string> var = 42; // 使用 get_if<> 来安全访问 int 类型
if (int* value = std::get_if<int>(&var)) {
std::cout << "Variant holds an int: " << *value << "\n";
} else {
std::cout << "Variant does not hold an int\n";
} // 尝试访问 double 类型,会返回 nullptr
if (double* value = std::get_if<double>(&var)) {
std::cout << "Variant holds a double: " << *value << "\n";
} else {
std::cout << "Variant does not hold a double\n";
} return 0;
}

输出:

Variant holds an int: 42
Variant does not hold a double

总结:

  • std::get_if<> 提供了类型安全且不抛异常的访问方式。
  • 如果匹配类型,它返回指向存储值的指针;否则返回 nullptr
  • 有两个版本:一个用于常量访问,另一个用于非常量访问。

std::variant快速上手的更多相关文章

  1. 【opencv入门篇】 10个程序快速上手opencv【下】

    导言:本系列博客目的在于能够在vs快速上手opencv,理论知识涉及较少,大家有兴趣可以查阅其他博客深入了解相关的理论知识,本博客后续也会对图像方向的理论进一步分析,敬请期待:) 上篇传送:http: ...

  2. 【opencv入门篇】 10个程序快速上手opencv【上】

    导言:本系列博客目的在于能够在vs快速上手opencv,理论知识涉及较少,大家有兴趣可以查阅其他博客深入了解相关的理论知识,本博客后续也会对图像方向的理论进一步分析,敬请期待:) PS:官方文档永远是 ...

  3. Markdown 语法的超快速上手

    本文支持WTFPL协议,因此你想往哪转就往哪转. Why markdown? Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式. Ma ...

  4. Pandas快速上手(一):基本操作

    本文包含一些 Pandas 的基本操作,旨在快速上手 Pandas 的基本操作. 读者最好有 NumPy 的基础,如果你还不熟悉 NumPy,建议您阅读NumPy基本操作快速熟悉. Pandas 数据 ...

  5. 快速上手pandas(上)

      pandas is a fast, powerful, flexible and easy to use open source data analysis and manipulation to ...

  6. 【Python五篇慢慢弹】快速上手学python

    快速上手学python 作者:白宁超 2016年10月4日19:59:39 摘要:python语言俨然不算新技术,七八年前甚至更早已有很多人研习,只是没有现在流行罢了.之所以当下如此盛行,我想肯定是多 ...

  7. 快速上手Unity原生Json库

    现在新版的Unity(印象中是从5.3开始)已经提供了原生的Json库,以前一直使用LitJson,研究了一下Unity用的JsonUtility工具类的使用,发现使用还挺方便的,所以打算把项目中的J ...

  8. [译]:Xamarin.Android开发入门——Hello,Android Multiscreen快速上手

    原文链接:Hello, Android Multiscreen Quickstart. 译文链接:Hello,Android Multiscreen快速上手 本部分介绍利用Xamarin.Androi ...

  9. [译]:Xamarin.Android开发入门——Hello,Android快速上手

    返回索引目录 原文链接:Hello, Android_Quickstart. 译文链接:Xamarin.Android开发入门--Hello,Android快速上手 本部分介绍利用Xamarin开发A ...

  10. 快速上手seajs——简单易用Seajs

    快速上手seajs——简单易用Seajs   原文  http://www.cnblogs.com/xjchenhao/p/4021775.html 主题 SeaJS 简易手册 http://yslo ...

随机推荐

  1. RHCA rh442 009 磁盘算法 RAID相关 磁盘压力测试

    磁盘 一个数据在磁盘A位置,一个数据在磁盘B位置,他们如果隔着很远.这对磁盘来说性能很差 (机械盘,磁头来回移动) 一个数据写进来,他会把数据放到缓存中,经过磁盘调度算法来调度,最后写到硬盘 io读写 ...

  2. 大语言模型可以自动生成sql语句吗?

    大语言模型的能力已经是毋庸置疑的了,随着ChatGPT的霸榜,各种语言模型的应用也多了起来,这时候突然有一个意外,那就是:大语言模型可以自动生成sql语句吗? 之所以有这个疑问,主要是因为sql正好是 ...

  3. 【转载】 ImportError: libGL.so.1: cannot open shared object file: No such file or directory——docker容器内问题报错

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_35516745/article/de ...

  4. 如何使用深度学习技术探测代码逻辑死循环 —— 浪潮集团的“公开号CN117271314A”专利

    专利公开号: CN117271314A 新闻链接: https://mbd.baidu.com/newspage/data/landingsuper?context={"nid"% ...

  5. OpenAI内讧更多细节曝光:奥特曼离间董事会失败

    参考: https://www.thepaper.cn/newsDetail_forward_25512687 ============================== 根据 https://ww ...

  6. ubuntu20.04/22.04 系统环境下源码编译Python3.10

    2022年10月3日更新 在Ubuntu22.04系统上源码编译python,所依赖环境的安装命令为: sudo apt install gcc g++ libffi-dev build-essent ...

  7. 掌握 Nuxt 3 的页面元数据:使用 definePageMeta 进行自定义配置

    title: 掌握 Nuxt 3 的页面元数据:使用 definePageMeta 进行自定义配置 date: 2024/8/11 updated: 2024/8/11 author: cmdrago ...

  8. abc366

    E 解题思路 这题求的是满足\(\sum^n_{i=1}(|x-x_i|+|y-y_i|)\leq D\) 的坐标\((x,y)\) 的数目,由于是求和,所以\(x,y\) 之间是相互独立的 第一步, ...

  9. RabbitMq高级特性之死信队列 通俗易懂 超详细 【内含案例】

    RabbitMq高级特性之死信队列 又称 死信交换机 DLX 介绍 当消息成为 Dead message 后,会重新发送到另一个交换机,这个交换机就是 DLX(死信交换机) 消息成为死信的情况公有三种 ...

  10. Win32 状态栏用法

    WIN32 状态控件用法 1.创建控件 状态栏类名: STATUSCLASSNAME #define STATUSCLASSNAMEW        L"msctls_statusbar32 ...