首页 > 编程语言 >C++17 std::variant 详解:概念、用法和实现细节

C++17 std::variant 详解:概念、用法和实现细节

时间:2024-09-27 18:11:56浏览次数:8  
标签:std 17 int visit variant 类型 include

std::variant 是C++17引入的一个新的标准库类型,它提供了一种类型安全的联合体。这个类可以在同一时间持有几种可能类型中的一个值。本文将详细介绍 std::variant 的概念、用法和实现细节。

1. 基本概念

std::variant 是一个模板类,可以存储几种不同类型中的一个值。它的声明如下:

template<class... Types>
class variant;

1.1 主要特性

  1. 类型安全:与C风格的联合体不同,std::variant 是类型安全的。
  2. 无默认构造:如果第一个类型不是默认构造的,那么 std::variant 也不是默认构造的。
  3. 不允许引用、数组和void:这些类型不能作为 std::variant 的可选类型。
  4. 可以为空:通过使用 std::monostate 作为第一个类型,std::variant 可以表示"无值"状态。

2. 基本用法

2.1 创建和访问

#include <variant>
#include <string>
#include <iostream>

int main() {
    std::variant<int, float, std::string> v;
    v = 42; // v 现在包含 int
    std::cout << std::get<int>(v) << std::endl; // 输出:42
    
    v = 3.14f; // v 现在包含 float
    std::cout << std::get<float>(v) << std::endl; // 输出:3.14
    
    v = "hello"; // v 现在包含 string
    std::cout << std::get<std::string>(v) << std::endl; // 输出:hello
    
    // 使用 std::get_if 安全地获取值
    if (const auto intPtr = std::get_if<int>(&v)) {
        std::cout << "It's an int: " << *intPtr << std::endl;
    } else {
        std::cout << "It's not an int" << std::endl;
    }
}

2.2 使用 std::visit

std::visit 允许我们以一种类型安全的方式访问 std::variant 中的值:

#include <variant>
#include <iostream>

struct Visitor {
    void operator()(int i) { std::cout << "It's an int: " << i << std::endl; }
    void operator()(float f) { std::cout << "It's a float: " << f << std::endl; }
    void operator()(const std::string& s) { std::cout << "It's a string: " << s << std::endl; }
};

int main() {
    std::variant<int, float, std::string> v = 42;
    std::visit(Visitor{}, v); // 输出:It's an int: 42
    
    v = 3.14f;
    std::visit(Visitor{}, v); // 输出:It's a float: 3.14
    
    v = "hello";
    std::visit(Visitor{}, v); // 输出:It's a string: hello
}

3. 高级用法

3.1 使用 std::monostate

std::monostate 可以用来表示 "无值" 状态:

#include <variant>
#include <iostream>

int main() {
    std::variant<std::monostate, int, std::string> v;
    if (std::holds_alternative<std::monostate>(v)) {
        std::cout << "The variant is empty" << std::endl;
    }
}

3.2 异常处理

当尝试访问错误的类型时,std::get 会抛出 std::bad_variant_access 异常:

#include <variant>
#include <iostream>
#include <stdexcept>

int main() {
    std::variant<int, std::string> v = 42;
    try {
        std::get<std::string>(v); // 这会抛出异常
    } catch (const std::bad_variant_access& e) {
        std::cout << "Exception: " << e.what() << std::endl;
    }
}

4. 实现细节

虽然具体实现可能因编译器而异,但以下是 std::variant 可能的实现概述:

4.1 内存布局

std::variant 通常使用以下内存布局:

  1. 一个足够大的未命名联合体,用于存储所有可能的类型。
  2. 一个整数索引,用于跟踪当前存储的类型。
template<class... Types>
class variant {
    union {
        Types... data;
    };
    size_t index;
    // ...
};

4.2 类型安全

类型安全通过以下方式实现:

  1. 使用模板元编程技术来确保只能存储允许的类型。
  2. 在运行时维护一个索引,指示当前存储的类型。

4.3 构造和赋值

构造和赋值操作涉及:

  1. 正确初始化或销毁联合体中的对象。
  2. 更新类型索引。

4.4 std::visit 的实现

std::visit 通常通过以下步骤实现:

  1. 使用类型索引确定当前存储的类型。
  2. 使用模板元编程生成一个 switch 语句或函数指针数组,以调用正确的访问器重载。

5. 性能考虑

  1. 内存使用std::variant 的大小等于最大可能类型的大小加上一些额外的存储(用于类型索引)。
  2. 访问开销:直接访问当前值通常只有很小的运行时开销。
  3. std::visit 开销std::visit 可能涉及一些运行时开销,特别是当处理大量可能类型时。

6. 最佳实践

  1. 优先使用 std::variant 而不是C风格的联合体,以获得类型安全性。
  2. 使用 std::get_if 进行安全的类型检查和访问。
  3. 利用 std::visit 进行通用的类型处理。
  4. 当需要表示 "无值" 状态时,考虑使用 std::monostate
  5. 注意处理 std::bad_variant_access 异常。

std::variant 是C++17引入的强大工具,为处理可能有多种类型的数据提供了类型安全和灵活的解决方案。理解其概念、用法和实现细节对于有效利用这一特性至关重要。

标签:std,17,int,visit,variant,类型,include
From: https://www.cnblogs.com/linxmouse/p/18436326

相关文章

  • 【TES817】l基于XCZU19EG FPGA的高性能实时信号处理平台
    板卡概述TES817是一款基于ZU19EGFPGA的高性能实时信号处理平台,该平台采用1片高性能的FPGA:XCZU19EG-2FFVC1760I作为主处理器,FPGA的PL端外挂1组72位DDR4SDRAM,用来实现超大容量数据缓存,FPGA的PS端外挂1组72位的DDR4SDRAM的高速数据缓存,用来支持操作系统的运行。该平台支持2个FMC......
  • GitLab 中文发行版最新的 17.4 发布了哪些与代码托管、CI/CD、安全合规相关的功能?
    沿袭我们的月度发布传统,极狐GitLab发布了17.4版本,该版本带来了当所有检查通过就会自动合并、WebIDE中的扩展市场现已可用以及获取Kubernetes资源事件等几十个重点功能的改进。下面是部分重点功能的详细解读。极狐GitLab为GitLab的中文发行版,中文版本对国内用户更友好、......
  • 易优CMS出现:Allowed memory size of 134217728 bytes exhausted (tried to allocate 2
    当你遇到“Allowedmemorysizeof134217728bytesexhausted(triedtoallocate20480bytes)”的错误时,这意味着PHP的内存限制已经耗尽。这种错误通常发生在处理大量数据或执行复杂计算时。为了解决这个问题,可以采取以下几种方法:方法1:修改 php.ini 文件(推荐)找到 php......
  • 易优CMS登录后台报Allowed memory size of 134217728 bytes ex hausted (tried to alo
    当你在登录后台时遇到“Allowedmemorysizeof134217728bytesexhausted(triedtoallocate20480bytes)”的错误提示时,通常是由于PHP的内存限制不足导致的。以下是一些具体的解决步骤:步骤1:检查PHP配置登录宝塔面板登录宝塔面板。在左侧菜单栏选择“软件商店”。......
  • 17 shutil 模块 文件操作 运维时会使用此模块
    4.2shutil模块文件操作运维时会使用此模块importshutil#删除目录常用返回值为None#shutil.rmtree('test')#重命名常用#shutil.move('test','ttt')#压缩文件#shutil.make_archive('zzh','zip','D:\code\s21day16\lizhong&......
  • LGB3717 题解
    原题链接:B3717组合数问题。难度:Easy组合数学的模板题。排除做法:\(n,m\le5\times10^6\),显然不能使用杨辉三角递推。模数为\(998,244,353\),无法使用\(\text{Lucas}\)定理。正解考虑直接使用组合数的计算式:\[{n\choosem}=\dfrac{n!}{m!(n-m)!}\]其中\(n!\)可......
  • 遇到stdole.dll强签名无效?Windows用户必看:如何安全处理stdole.dll的强签名验证问题
    遇到stdole.dll强签名无效的问题时,Windows用户需要谨慎处理以确保系统的稳定性和安全性。以下是一些安全处理stdole.dll强签名验证问题的步骤和建议:一、了解stdole.dll首先,了解stdole.dll是一个关键的系统文件,它主要负责OLE(对象链接与嵌入)技术的一些基础功能。在Windows操作......
  • AT_arc176_e [ARC176E] Max Vector 题解
    发现数据范围很小,考虑最小割。先对题面做一个转化:构造两个序列\(X=(X_1,X_2,\dots,X_N),Y=(Y_1,Y_2,\dots,Y_N)\)最小化\(\sumX_i+Y_i\),有\(M\)个限制,每个限制有一个序列\(A_1,A_2,\dots,A_n\),需要满足\(\foralli,X_i\geA_i\)或者\(\foralli,Y_i\geA_i\)。考虑怎......
  • 洛谷题单指南-分治与倍增-P3517 [POI2011] WYK-Plot
    原题链接:https://www.luogu.com.cn/problem/P3517题意解读:有n个连续的点p1,p2,...,pn,将这n个点分成不超过m堆,每堆点连续,每一堆都缩成一个点qi,要使得原来的点p1~ps距离qi的最大值最小(最相似),求这个相似度,并计算一共分成几堆,以及每堆缩到的点qi的坐标。解题思路:要使得若干点缩到一......
  • FastDFS+Nginx+fastdfs-nginx-module集群搭建
    一、实验环境说明 操作系统:Centos6.6x64FastDFS相关版本:fastdfs-5.05fastdfs-nginx-module-v1.16libfastcommon-v1.0.7web服务器软件:nginx-1.7.8角色分配:2个tracker,地址分别为:10.1.1.24310.1.1.244两块磁盘2个group:G1:10.1.1.24510.1.1......