std::variant快速上手

chenchen / 2024-09-19 / 原文

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 处理不同类型的值。