# std::variant用法

# Union类型

与union类型作对比,union类型是为了在同块内存中支持多种类型数据的场景,譬如一块内存既可能放char又可能放int,那就可以定义成Union类型:

#include <cstdio>

int main(void) {
    union U1
    {
        int a;
        char b;
        double c;
    };
    
    U1 u1;
    u1.a = 1000;
    printf("u1 %d u1.b %d\n", u1.a, *reinterpret_cast<int*>(&(u1.b)));
    printf("u1 %x u1.a %x u1.b %x u1.c %x\n",
         &u1, &(u1.a), &(u1.b), &(u1.c));
    return 0;
}

// u1 1000 u1.b 1000
// u1 302f1bf0 u1.a 302f1bf0 u1.b 302f1bf0 u1.c 302f1bf0

可以看到u1, u1.a,u1.b,u1.c都指向相同的地址,即Union类型的首地址.

# C++17中引入的variant类型

variant属于 C++ 标准库中的 头文件中,std::variant是一个类型安全的联合体,可以存储固定集合中的任意类型的值。这使得 std::variant 成为处理那些可能需要存储不同类型数据的情况的理想选择。

特点:

  • 1.类型安全: 与传统的 C 联合体(union)不同,std::variant 在类型安全方面提供了显著的改进。它能保证在任何时候都只包含其能持有的类型之一,并且提供了丰富的接口来检查和访问存储的数据。

  • 2.自动管理: std::variant 自动处理类型的构造、析构和赋值,确保资源的正确管理。

  • 3.访问控制: 提供了安全的方式访问存储的数据,例如std::getstd::visit等函数。

访问元素

std::get:可以通过 std::get<Type>(variant) 获取 variant 中存储的类型为 Type 的值。如果 variant 当前不持有该类型,则会抛出 std::bad_variant_access 异常。

std::visit应用一个访问者(通常是一个 lambda 表达式或函数对象)到 variant 中存储的值上

std::holds_alternative使用std::holds_alternative<T>(v)函数,这个函数返回一个布尔值,表示std::variant是否当前持有类型T.

std::get_if提供了一种安全的方式来尝试获取std::variant中存储的值,而不会抛出异常。它返回指向存储的值的指针,如果std::variant当前不持有请求的类型,则返回nullptr


#if defined(__cplusplus)
#if __cplusplus == 201703L
#include <variant>
#include <string>
#include <iostream>
void VariantTest() {
    // 定义一个可以存储 int 或 double 或 std::string 的 variant
    std::variant<int, double, std::string> v;
    v = 10;
    printf("int: %d\n", std::get<int>(v));
    v = 12.3;
    printf("double: %f\n", std::get<double>(v));
    v = "string";
    printf("string: %s\n", std::get<std::string>(v).c_str());
    
    try {
        printf("int: %d\n", std::get<int>(v)); // 这将抛出异常,因为当前存储的是 string
    } catch (const std::bad_variant_access&) {
        printf("Error: The current variant does not hold an int.\n");
    }

    std::visit([](auto && arg) {
        std::cout << "arg: "  << arg << std::endl;
    }, v);

    if (std::holds_alternative<std::string>(v)) {
        std::cout << "Variant holds a string." << std::endl;
    } else {
        std::cout << "Variant does not hold a string." << std::endl;
    }

    if (auto val = std::get_if<int>(&v)) {
        std::cout << "The value is: " << *val << std::endl;
    } else {
        std::cout << "Variant does not hold an int." << std::endl;
    }

    printf("sizeof v: %lu\n", sizeof(v));
// int: 10
// double: 12.300000
// string: string
// Error: The current variant does not hold an int.
// arg: string
// Variant holds a string.
// Variant does not hold an int.
// sizeof v: 40
}

# reference