std::variant 与 std::visit

本文介绍了C++17中的std::variant,它比C语言的Union功能更强大,并讨论了其使用方法,包括std::variant的声明、类型判断和获取值。同时,文章探讨了std::visit作为访问std::variant的高效方式,对比了不同访问方法的优缺点,并展示了std::visit在处理多个variant变量时的灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

std::variant

简介

std::variant 是c++17 引入的一个类型,其作用类似于C语言中的Union,但是比Union 的功能强大的多。
C语言中一个联合体Union 可以储存多种类型数据,但缺点有很多。比如:
1 没有可用的方法来判断Union中真实储存的类型,获取值时也是内存拷贝的结果,可能会存在问题。这就只能靠程序员人脑保证获取的值是有意义的。
2 只能储存基础数据类型,不能储存其他结构体

使用 std::variant

声明一个variant对象很容易,我们可以利用std::variant 和 std::vector 将不同类型的数据放到一起,形成类似弱类型语言的效果 (例如javascript),这一点将非常有用。

#include <variant>

   using value = std::variant<int, double, bool, std::string>;
   std::vector<value> values = {
   0, true, 3.1415926,"hello"};

如何取出一个std::variant对象的值呢?
有一系列的方法判断一个 std::variant 对象的类型, 例如

  1. index() 方法, 返回 variant 对象类型index,
    当一个std::variant 对象没有被初始化的时候,会发生什么呢?会默认按照第一个类型进行初始化,那如果第一个类型没有默认的构造函数会怎么样呢?编译器会报错,为了解决这个问题,std::引入了 std::monostate 作为占位的类型。也可以使用它来替代一部分 std::optional 的功能。

  2. holds_alternative<> 判断 variant 是否是某种特定类型

  3. std::get<> 方法获取类型的值,类型不正确会抛exception,模板参数可以是类型,也可以是 index值, 但必须是常量值,因为模板是编译期推断

  4. std::get_if<> 推荐使用的方法,大部分c++项目都是禁止try- catch 的,因此无异常版本更加实用。
    该方法在类型不正确会返回空,输入和输出都是指针类型


#include <variant>

using value = std::variant<int, double, bool, std::string>;
value = "hello, world";
std::cout<<value.index()<<std::end;  //3, index 从0 开始

std::cout<<std::holds_alternative<std::string><<std::end;  //true

std::get<3>(v
### C++ 中 `std::variant` 的用法及其常见错误解决方案 #### 什么是 `std::variant` `std::variant` 是一种类型安全的联合体(union),它允许存储多种类型的值之一。它是自 C++17 起引入的标准库组件,提供了更灵活的方式处理多态数据结构[^3]。 以下是其主要特性: - 它可以保存一组预定义类型中的任意一个。 - 当前活动的类型可以通过访问器函数获取。 - 如果尝试访问未激活的类型,则会抛出异常 `std::bad_variant_access`。 #### 基本语法示例 下面是一个简单的例子展示如何声明和使用 `std::variant`: ```cpp #include <iostream> #include <variant> int main() { std::variant<int, double, std::string> v; // 设置 int 类型 v = 42; if (std::holds_alternative<int>(v)) { std::cout << "Value is an integer: " << std::get<int>(v) << '\n'; } // 设置 double 类型 v = 3.14; if (std::holds_alternative<double>(v)) { std::cout << "Value is a double: " << std::get<double>(v) << '\n'; } // 设置 string 类型 v = "Hello"; if (std::holds_alternative<std::string>(v)) { std::cout << "Value is a string: " << std::get<std::string>(v) << '\n'; } } ``` 上述代码展示了如何通过 `std::get<T>` 获取特定类型的值以及如何利用 `std::holds_alternative<T>` 来判断当前存储的是哪种类型[^3]。 #### 访问控制机制 为了简化不同类型的访问操作,标准库还提供了一个辅助工具——`std::visit` 函数模板。它可以接受一个可调用对象作为参数,并将其应用于 `std::variant` 所持有的具体类型上。 以下是如何结合 `std::visit` 使用的例子: ```cpp #include <iostream> #include <variant> #include <string> struct PrintVisitor { template<typename T> void operator()(T&& arg) const { std::cout << arg << "\n"; } }; int main() { std::variant<int, float, std::string> var = "Example"; std::visit(PrintVisitor{}, var); // 输出 Example } ``` 这里我们创建了一个通用访客类来打印任何可能被存入 variant 的类型的数据[^4]。 #### 错误案例分析及解决方法 当使用 `std::variant` 时可能会遇到一些常见的问题,比如试图访问不存在的类型或者忘记初始化变量等。这些问题通常会导致运行期错误甚至程序崩溃。 ##### 案例一:非法访问引发异常 如果直接调用了不匹配类型的 getter 方法而没有先验证该类型是否存在的话,就会触发 `std::bad_variant_access` 异常。 **修复建议**: 总是在访问之前确认所需类型确实存在再继续下一步动作。 ```cpp try { auto value = std::get<float>(var); } catch(const std::bad_variant_access& e){ std::cerr << "Error accessing wrong type." << std::endl; } ``` ##### 案例二:默认构造行为不明朗 某些情况下,默认构造出来的 variant 可能处于不确定状态,这取决于编译器实现细节。 **最佳实践**: 明确指定初始值或确保所有潜在选项都有合理缺省设置。 ```cpp // 正确做法 std::variant<int, std::string> safeVar{0}; // 避免这种模糊情况 std::variant<int, std::string> unsafeVar; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值