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

news2024/11/26 14:51:27

文章目录

  • 前言与需求
  • 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.coloradmin.cn/o/1190742.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

『亚马逊云科技产品测评』活动征文|AWS 云服务器实例类型及其适用场景详细说明

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 目录 一、AWS 定价计算器 &#xff08;1&#xff09;定价计算器官网 &…

Linux环境配置(云服务器)

目录 1.第一步&#xff1a;购买云服务器 2.第二步&#xff1a;下载Xshell 7 3.第三步&#xff1a;打开Xshell&#xff0c;登录云服务器 4.第四步&#xff1a;更加便捷的云服务器登录方式 1.第一步&#xff1a;购买云服务器 &#xff08;推荐&#xff1a;阿里云、华为云、腾…

机器视觉工程师注意,没有经历过公司倒闭看下文章,机器视觉公司即将要倒闭的征兆是什么?

很多机器视觉工程师没有经历过公司倒闭&#xff0c;谁也不想自己的公司倒闭&#xff0c;毕竟我们是打工人&#xff0c;拿固定工资的。 机器视觉公司即将要倒闭的征兆有哪些迹象​&#xff1f;​ 1、PM&#xff0c;机器视觉工程师频繁开会&#xff0c;甚至周末强制开会。 2.停…

P6入门:项目初始化1-项目详情介绍

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…

Zigbee—网络层地址分配机制

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;孤雏 0:21━━━━━━️&#x1f49f;──────── 4:14 &#x1f504; ◀️ ⏸ ▶️ ☰ &#x1f497;关注…

进行 “最佳价格查询器” 的开发

前置条件 public class Shop {private final String name;private final Random random;public Shop(String name) {this.name name;random new Random(name.charAt(0) * name.charAt(1) * name.charAt(2));}public double getPrice(String product) {return calculatePrice…

『 C++类与对象 』多继承与虚继承

文章目录 ⌨️多继承的概念语法 &#x1f5b1;️ ⌨️棱形继承⌨️虚继承虚继承是如何解决数据冗余和二义性的(不谈虚表概念)?&#x1f5b1;️ ⌨️多继承的概念 多继承指的是一个派生类是由多个基类继承而来的; 而在生活当中也有类似的例子:番茄既可以是水果,也可以是蔬菜;…

[100天算法】-面试题 17.11.单词距离(day 68)

题目描述 有个内含单词的超大文本文件&#xff0c;给定任意两个单词&#xff0c;找出在这个文件中这两个单词的最短距离(相隔单词数)。如果寻找过程在这个文件中会重复多次&#xff0c;而每次寻找的单词不同&#xff0c;你能对此优化吗?示例&#xff1a;输入&#xff1a;word…

华为Auth-Http Serve任意文件读取

1.漏洞描述 华为Auth-Http Server 1.0任意文件读取&#xff0c;攻击者可通过该漏洞读取任意文件。 2.网络资产查找 FOFA&#xff1a;server”Huawei Auth-Http Server 1.0” 2、部分界面如下 3、Poc /umweb/shadow 4、Poc批量验证 id: huanwei-auth-http-server-filereadi…

商业模式思维导图

一、什么是商业模式&#xff1f;简单的说&#xff0c;商业模式就是公司通过什么途径或方式来赚钱&#xff1f; 商业模式的核心是 创造价值-获取价值-传递价值。 它有3个重要的环节&#xff0c;创造价值是基于客户需求&#xff0c;提供解决方案&#xff0c;传递价值是通过资源配…

5个写自定义函数小练习

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

Python克隆单个网页

网上所有代码都无法完全克隆单个网页&#xff0c;不是Css&#xff0c;Js下载不下来就是下载下来也不能正常显示&#xff0c;只能自己写了&#xff0c;记得点赞~ 效果如图&#xff1a; 源码与所需的依赖&#xff1a; pip install requests pip install requests beautifulsoup4…

ThreadLocal原理以及内存泄露问题

1、ThreadLocal实现原理 1、每个线程中有一个ThreadLocalsMap&#xff0c;这是一个哈希表的结构里面有很多entry(也就是k-v)&#xff0c;当我们使用ThreadLocal进行set值的时候,会将这个threadLocal设置为key,然后值设置为value放入ThreadLocalsMap&#xff0c;key为弱引用&am…

Vue计算属性 computed

计算属性&#xff1a;常用于逻辑比较复杂的计算&#xff0c;根据已有的数据&#xff0c;计算出一个新的数据。计算属性具有缓存机制&#xff0c;复用性强&#xff0c;效率更高。 计算属性【简写方式】&#xff1a; <template><div>提现金额&#xff1a;<input …

qt-C++笔记之Qt中的时间与定时器

qt-C笔记之Qt中的时间与定时器 code review! 文章目录 qt-C笔记之Qt中的时间与定时器一.Qt中的日期时间数据1.1.QTime&#xff1a;获取当前时间1.2.QDate&#xff1a;获取当前日期1.3.QDateTime&#xff1a;获取当前日期和时间1.4.QTime类详解1.5.QDate类详解1.6..QDateTime类…

ChatGPT付费创作系统V2.4.9独立版 +WEB端+ H5端 + 小程序端系统测试安装教程

播资源提供的GPT付费体验系统最新版系统是一款基于ThinkPHP框架开发的AI问答小程序&#xff0c;是基于国外很火的ChatGPT进行开发的Ai智能问答小程序。当前全民热议ChatGPT&#xff0c;流量超级大&#xff0c;引流不要太简单&#xff01;一键下单即可拥有自己的GPT&#xff01;…

不定积分第一类换元法(凑微分法)

将其中的 分解为 相当于 令 那么. 就可以得到 例题1 令 那么 因为 所以 利用基本积分公式 结果 例题2 上下同除 接下来需要一些技巧 这个形式需要联想到一个基本积分公式 不巧是这里是2不是1需要利用技巧把2变成1

GDPU 数据结构 天码行空9

实验九 哈夫曼编码 一、【实验目的】 1、理解哈夫曼树的基本概念 2、掌握哈夫曼树的构造及数据结构设计 3、掌握哈夫曼编码问题设计和实现 二、【实验内容】 1、假设用于通信的电文仅由8个字母 {a, b, c, d, e, f, g, h} 构成&#xff0c;它们在电文中出现的概率分别为{ 0.…

拍摄视频的时候相机断电导致视频文件损坏,怎么修复

3-4 现在好多人都有自己的相机&#xff0c;但是专业用来录像的机器应该是不太可能都有的&#xff0c;相机的稳定性会比专业的机器差一些&#xff0c;如果用于比较重要的场景&#xff0c;比如婚庆、会议录像、家庭录像使用等&#xff0c;有较少的概率会出现一些奇怪的情况&…

mysql千万数据快速插入-实战

文章目录 前言环境一、配置二、效果总结 前言 数据量太大了&#xff0c;每天半夜要同步很大数据到 mysql 数据库&#xff0c;其中一张表就上2千万&#xff0c;总计上亿条数据。同步任务每天0点之后开始任务&#xff08;因为到0之后才能统计前一天数据&#xff09;&#xff0c;…