(C++17) variant的使用与union对比

文章目录

  • 前言与需求
  • union
    • 内存映射图
    • C++11的union
  • 使用
    • ref示例
    • 构造
      • 普通构造
      • 置空
      • emplace
      • monostate
    • 访问
      • std::get<>
      • std::holds_alternative<>
      • 获取指针std::get_if<>
      • 获取可选数量个数std::variant_size
  • END

前言与需求

联合体,是在C语言时代就存在的概念。主要应用在一些收内存限制较大的情景。

但是传统C的限制太大,但是到了C++中给出了更安全的类型std::variant

union

内存映射图

先来看一张C语言数据结构内存映射图

!网图(非原创),侵删!

在这里插入图片描述

在union中所有字段是公用一块内存区域的。

C++11的union

在旧时union中成员不能是一个非凡类型。到了C++11解除了限制,只要是非应用类型即可。

其实union也可以单独开一篇文章来讲,这里给个简单示例,不做过多讲解:

#include <iostream>
#include <string>
#include <vector>

union U {
    U() {
    }
    ~U() {
    }

    static int s;

    int   x;
    float y;

    std::string      str;
    std::vector<int> vec;
};
// 同类的静态成员类似
int U::s = 10;

int main() {
    std::cout << U::s << std::endl;

    U u;

    // 使用 placement new 和 手动析构
    new (&u.str) std::string("hello world");
    std::cout << u.str << std::endl;
    u.str.~basic_string();

    new (&u.vec) std::vector<int>(2, -1);
    u.vec.push_back(1);
    for (int i = 0; i < u.vec.size(); i += 1) {
        std::cout << u.vec[i] << std::endl;
    }
    u.vec.~vector();

    return 0;
}

使用

std::variant - cppreference.com

ref示例

std::variant 是类型安全的,只能访问一种类型

#include <cassert>
#include <string>
#include <variant>

int main() {
    std::variant<int, float> v, w;
    v     = 12;  // v 含 int
    int i = std::get<int>(v);
    w     = std::get<int>(v);
    w     = std::get<0>(v);  // 与前一行效果相同
    w     = v;               // 与前一行效果相同

    //  std::get<double>(v); // 错误: [int, float] 中无 double
    //  std::get<3>(v);      // 错误:合法下标值为 0 与 1

    try {
        std::get<float>(w);  // w 含 int 而非 float :将抛出
    } catch (const std::bad_variant_access&) {
    }

    using namespace std::literals;

    std::variant<std::string> x("abc");  // 转换构造函数在无歧义时起作用
    x = "def";  // 转换赋值在无歧义时亦起作用

    std::variant<std::string, void const*> y("abc");
    // 传递 char const * 时转换成 void const *
    assert(std::holds_alternative<void const*>(y));  // 成功
    y = "xyz"s;
    assert(std::holds_alternative<std::string>(y));  // 成功
}

构造

直接赋值即可。

普通构造

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

void fun() {
    // 可以相同
    std::variant<int, int> var;
    // 操作比较特殊
}

int main() {
    std::variant<std::string, int> empty;
    
    std::variant<std::string, int> var("abc");
    std::cout << std::get<0>(var) << std::endl;

    var = 123;
    std::cout << std::get<1>(var) << std::endl;
    std::get<1>(var) = 654321;
    std::cout << std::get<1>(var) << std::endl;
}

置空

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

int main() {
    std::variant<int, std::string> v = "abc";
    // v.index = 1
    std::cout << "v.index = " << v.index() << '\n';

    // 置空了
    v = {};
    // v.index = 0
    std::cout << "v.index = " << v.index() << '\n';
}

emplace

可以原地构造

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

int main() {
    std::variant<std::string> var;
    var.emplace<0>(2, 'a');
    std::cout << std::get<0>(var) << std::endl;

    var.emplace<std::string>("Hello World");
    std::cout << std::get<std::string>(var) << std::endl;
}

monostate

有一类情况比较特殊。

有意为行为良好的 std::variant 中空可选项所用的单位类型。具体而言,非可默认构造的 variant 可以列 std::monostate 为其首个可选项:这使得 variant 自身可默认构造。

  • std::monostate
  • valueless_by_exception()
#include <iostream>
#include <variant>

struct S {
    int value;
    S(int i) : value(i) {
        std::cout << __func__ << std::endl;
    }
};

int main() {
    // 若无 monostate 类型则此声明将失败。
    // 这是因为 S 不可默认构造。

    std::variant<std::monostate, S> var;
    // 不保有值
    std::cout << std::boolalpha << var.valueless_by_exception() << std::endl;

    // std::get<S> 将抛异常!我等需要赋一个值
    // var.index() 现为 0 ——首个元素
    std::cout << "cur index = " << var.index() << '\n';
    var = 12;
    std::cout << std::get<S>(var).value << '\n';
}

访问

std::get<>

请注意同类型的情况

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

void fun0() {
    // 可以相同
    std::variant<int, int> var;

    // 编译不通过,非动态错误
    // var = 10;

    // 编译不通过,非动态错误
    // std::get<int>(var) = 100;

    // 下标访问可行
    std::get<0>(var) = 10;

    try {
        std::cout << std::get<1>(var) << std::endl;
    } catch (const std::bad_variant_access& e) {
        // std::get: wrong index for variant
        std::cout << e.what() << std::endl;
    }
}

void fun1() {
    std::variant<std::string, int> var("abc");

    std::cout << std::get<0>(var) << std::endl;
    std::cout << std::get<std::string>(var) << std::endl;
}

int main() {
    fun0();
    fun1();
}

std::holds_alternative<>

判断是否可以转换

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

int main() {
    std::cout << std::boolalpha;

    std::variant<std::string, int> var("abc");

    std::cout << "int " << std::holds_alternative<int>(var) << std::endl;
    std::cout << "string " << std::holds_alternative<std::string>(var)
              << std::endl;
}

获取指针std::get_if<>

#include <iostream>
#include <variant>

int main() {
    std::variant<int, float> var;
    std::cout << &var << std::endl;

    // 整形
    var       = 12;
    auto pval = std::get_if<int>(&var);
    std::cout << pval << std::endl;
    if (pval) {
        std::cout << "variant value: " << *pval << '\n';
    } else {
        std::cout << "failed to get value!" << '\n';
    }

    // 浮点数
    var        = 12.3f;
    auto pval2 = std::get_if<float>(&var);
    std::cout << pval2 << std::endl;
    if (pval2) {
        std::cout << "variant value: " << *pval2 << '\n';
    } else {
        std::cout << "failed to get value!" << '\n';
    }
}
0xc10cbffba0
0xc10cbffba0
variant value: 12
0xc10cbffba0
variant value: 12.3

获取可选数量个数std::variant_size

在编译器确定

  • std::variant_size
  • std::variant_size_v
#include <any>
#include <cstdio>
#include <variant>

// 全部 pass

static_assert(std::variant_size_v<std::variant<>> == 0);
static_assert(std::variant_size_v<std::variant<int>> == 1);
static_assert(std::variant_size_v<std::variant<int, int>> == 2);
static_assert(std::variant_size_v<std::variant<int, int, int>> == 3);
static_assert(std::variant_size_v<std::variant<int, float, double>> == 3);
// std::monostate 也算一个占位
static_assert(std::variant_size_v<std::variant<std::monostate, void>> == 2);
static_assert(std::variant_size_v<std::variant<const int, const float>> == 2);
static_assert(std::variant_size_v<std::variant<std::variant<std::any>>> == 1);

int main() {
    std::puts("All static assertions passed.");
}



END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/125878.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Global_Mapper_Pro_25.0安装教程大全

一. 下载&#xff1a; http://dt1.8tupian.net/2/29913a55b1000.pg3二. 介绍&#xff1a; Global Mapper Pro 25是领先的GIS数据处理解决方案&#xff01;提供了一整套符合标准的功能来提升您的操作和技能&#xff0c;您可以最合理的利用您的工具集来完成以前复杂的工作任务&a…

矢量图形编辑软件Boxy SVG mac中文版软件特点

Boxy SVG mac是一款基于Web的矢量图形编辑器&#xff0c;它提供了一系列强大的工具和功能&#xff0c;可帮助用户创建精美的矢量图形。Boxy SVG是一款好用的软件&#xff0c;并且可以在Windows、Mac和Linux系统上运行。 Boxy SVG mac软件特点 简单易用&#xff1a;Boxy SVG的用…

说说对React中类组件和函数组件的理解?有什么区别?

一、类组件 类组件&#xff0c;顾名思义&#xff0c;也就是通过使用ES6类的编写形式去编写组件&#xff0c;该类必须继承React.Component 如果想要访问父组件传递过来的参数&#xff0c;可通过this.props的方式去访问 在组件中必须实现render方法&#xff0c;在return中返回…

【LLMs】从大语言模型到表征再到知识图谱

从大语言模型到表征再到知识图谱 InstructGLMLLM如何学习拓扑&#xff1f;构建InstructGLM泛化InstructGLM补充参考资料 2023年8月14日&#xff0c;张永峰等人的论文《Natural Language is All a Graph Needs》登上arXiv街头&#xff0c;轰动一时&#xff01;本论文概述了一个名…

带头+双向+循环链表

前言&#xff1a; 前面我们已经学习了单链表的结构及其功能特点&#xff0c;也了解了单链表在实现一些功能时出现的一些缺点&#xff0c;比如在删除某个节点前面一个节点时就需要再开一个变量来存放前面一个节点的信息&#xff0c;这样就显得不灵活&#xff0c;为了使链表实现功…

“可一学院”新课程《区块链企业应用》正式上线

2023年8月&#xff0c;上海可一澈科技有限公司启动了一站式区块链学习平台“可一学院BitClass”。9月6日&#xff0c;可一学院正式推出一门新课程《区块链企业应用》&#xff0c;这门课程将帮助学习者了解企业需要什么样的区块链&#xff0c;以及应该如何运用这项技术来推动自身…

GIT的安装与常见命令

Git的介绍 Git是一个开源的分布式版本控制系统&#xff0c;最初由Linus Torvalds在2005年创建用于管理Linux内核的开发&#xff0c;现在已成为全球最流行的版本控制工具之一。 Git可以跟踪代码的修改&#xff0c;记录开发历程&#xff0c;保证多人合作开发时代码的一致性&…

5个写自定义函数小练习

计算列表平均值、素数判定、反转字符串&#xff0c;查找整数列表最大最小值、统计字符串中元音字母个数(大小写字不敏感)。 (笔记模板由python脚本于2023年11月09日 21:50:35创建&#xff0c;本篇笔记适合熟悉Python函数及基本数据类型的coder翻阅) 【学习的细节是欢悦的历程】…

使用反射来遍历Java对象类中的所有属性名和属性值

有些时候我们需要获取到一个对象中的所有属性名和属性值&#xff0c;对其值进行操作&#xff0c;例如判断对象中某个属性是否是空值。 这种时候我们再使用get(),set()来进行操作就会有些麻烦了。 因此我们可以选择使用反射来进行遍历对象中的所有属性名和属性值。在遍历中编写逻…

陪玩2.0升级版源码/价值18500元的最新商业版游戏陪玩语音聊天系统源码

陪玩2.0升级版源码&#xff0c;价值18500元的最新商业版游戏陪玩语音聊天系统源码。 修复部分逻辑以及bug 修复bug&#xff1a;店员拒单后&#xff0c;退款会退到店员账号里而不是用户账户里。 修复bug&#xff1a;客户在盲盒下单后&#xff0c;马上取消了订单&#xff0c;但…

[100天算法】-定长子串中元音的最大数目(day 67)

题目描述 给你字符串 s 和整数 k 。请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。英文中的 元音字母 为&#xff08;a, e, i, o, u&#xff09;。示例 1&#xff1a;输入&#xff1a;s "abciiidef", k 3 输出&#xff1a;3 解释&#xf…

<C++> list模拟实现

目录 前言 一、list的使用 1. list的构造函数 2. list iterator的使用 3. list capacity 4. list modifiers 5. list的算法 1. unique​ 2. sort 3. merge 4. remove 5. splice 二、list模拟实现 1. 设置节点类 && list类 2. push_back 3. 迭代器 重载 * 重载前置 …

os_cfg.h、os_cpu.h和ucos_ii.h

目录 文件组织代码研读#ifndef OS_CFG_H#if OS_TASK_STAT_EN > 0u 文件组织 os_cfg.h 用于定义操作系统&#xff08;OS&#xff09;的配置参数&#xff0c;例如任务数量、堆栈大小、时间片大小等。它通常包含了用户可以根据需求进行配置的宏定义。os_cpu.h 用于定义与特定CP…

Centos批量删除系统重复进程

原创作者&#xff1a;运维工程师 谢晋 Centos批量删除系统重复进程 客户一台CENTOS 7系统负载高&#xff0c;top查看有很多sh的进程&#xff0c;输入命令top -c查看可以看到对应的进程命令是/bin/bash     经分析后发现是因为该脚本执行时间太长&#xff0c;导致后续执…

11-09 周四 CNN 卷积神经网络基础知识

11-09 周四 CNN 卷积神经网络 时间版本修改人描述2023年11月9日09:38:12V0.1宋全恒新建文档 简介 学习一下CNN&#xff0c;卷积神经网络。使用的视频课程。视觉相关的任务&#xff1a; 人脸识别 卷积网络与传统网络的区别&#xff1a; <img altimage-20231109094400591 s…

《011.SpringBoot+vue之汽车销售管理系统》

《011.SpringBootvue之汽车销售管理系统》 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatis; 前台&#xff1a;vueElementUI; [2]功能模块展示&#xff1a; 1.登录 2.销…

MySQL表的增删改查(进阶)

1. 数据库约束 1.1 约束类型 NOT NULL - 指示某列不能存储 NULL 值。 UNIQUE - 保证某列的每行必须有唯一的值。 DEFAULT - 规定没有给列赋值时的默认值。 PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列&#xff08;或两个列多个列的结合&#xff09;有唯一标识&#…

Go并发编程(上)

目录 一、go语言当中的协程 二、MPG模型介绍 三、Goroutine 的使用 3.1 协程的开启 3.2 优雅地等待子协程结束 四、捕获子协程的panic 五、管道Channel 5.1、认识管道 5.2、Channel的遍历和关闭 5.3 、用管道实现生产者消费者模型 5.4、Channel一些使用细节和注意事…

交叉编译中常见错误解决方法

目录 程序运行基础知识 编译程序时去哪找头文件&#xff1f; 链接时去哪找库文件&#xff1f; 运行时去哪找库文件&#xff1f; 运行时不需要头文件&#xff0c;所以头文件不用放到板子上 常见错误的解决方法 头文件问题 库文件问题 运行问题 交叉编译程序的万能命令 …

开源论道 源聚一堂@COSCon

自2015年以来&#xff0c;开源高峰论坛一直是中国开源年会中的传统亮点项目。本次在COSCon23 大会期间的高峰圆桌会&#xff0c;于2023年10月29日在成都高新区的菁蓉汇召开。 本次高峰圆桌上&#xff0c;我们特别邀请了20 位来自企业&#xff0c;基金会和社区的专家和领袖参加讨…
最新文章