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. 巧用 QLineF 从 QTransform 提取角度

    我们在对 QGraphicsItem 进行变换时,QT 提供了很多便捷的方法.但当我们想获取当前变换的角度时却有些困难,因为 QTransform 没有提供获取角度的方法.在文章Qt 从 QTrans ...

  2. android:inputType 类型详细介绍

    //文本类型,多为大写.小写和数字符号.android:inputType="none"//输入普通字符android:inputType="text"//输入 ...

  3. 信奥生(OIER)请看,包囊初赛复赛全真模拟赛!

    luogu 动态追踪! 唠唠嗑 感谢 tyw 代理团主对比赛的贡献,但是由于我和 tyw 的关系紧张,tyw 取消了我和她的一切合作.CTFPC-3rd 的出题.宣传工作都交到了我手上,我这次亚历山大 ...

  4. 【Java】【常用类】LocalDateTime 当前日期时间类 相关

    LocalDate主要的三个API类: java.time.LocalDate; java.time.LocalDateTime; java.time.LocalTime; LocatDate对象获取 ...

  5. 【Mybatis-Plus】05 条件构造器 ConditionConstructor

    理解: 原来叫条件构造器,我一直以为都是封装条件对象 即SQL的查询条件,不过都一样. 其目的是因为的实际的需求灵活多变,而我们的SQL的筛选条件也需要跟着变化, 但是有一些固定的字段固定的方式可以保 ...

  6. 使用AI技术(单张图片或文字)生产3D模型 —— Ai生成3D模型的时代来了

    地址: https://www.bilibili.com/video/BV1A2421P7pH/ 视频用到的工具voxcraft体验地址:https://voxcraft.ai/

  7. 【转载】 pytorch reproducibility —— pytorch代码的可复现性

    原文地址: https://www.jianshu.com/p/96767683beb6 作者:kelseyh来源:简书 ======================================= ...

  8. Ubuntu Firefox浏览器播放视频报错,提示“需要安装所需的视频编码器”——解决方法:安装视频解码器

    给电脑重新做了一个Ubuntu的系统,安装系统的时候没有选择安装第三方软件,结果开机进系统打开firefox浏览器看个电影报错,提示"需要安装所需的视频编码器",效果如下: 解决方 ...

  9. python语言绘图:绘制贝叶斯方法中最大后验密度(Highest Posterior Density, HPD)区间图的近似计算(续)

    代码源自: https://github.com/PacktPublishing/Bayesian-Analysis-with-Python 内容接前文: python语言绘图:绘制贝叶斯方法中最大后 ...

  10. 一直让 PHP 程序员懵逼的同步阻塞异步非阻塞,终于搞明白了

    大家好,我是码农先森. 经常听到身边写 Java.Go 的朋友提到程序异步.非阻塞.线程.协程,让系统性能提高到百万.千万并发,使我甚是惊讶属实羡慕.对于常年写 PHP 的我来说,最初听到这几个词时, ...