Modern C++ std::variant的5个特性+原理

1 前言

上一节《Modern C++ std::variant的实现原理》我们简单分析了std::variant的实现原理,其实要学好C++编程,除了看优秀的代码包括标准库实现,读文档也是很便捷且必须的一种办法。
本节我将逐条解析文档中的五个特性,解析的办法有两种:实现代码讲解、用例子举例。

2 文档

variant文档
在这里插入图片描述

3解析

以下所有实现代码都来自/usr/include/c++/11/variant。

3.1 类型安全

The class template std::variant represents a type-safe union.
An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value (this state is hard to achieve, see valueless_by_exception).
类型安全,且总会持有一种类型的值,但也有极小的可能无值(valueless)。
无值请参考文档, 我们重点说下类型安全。
咱们先说下union怎么类型不安全,比如下面的例子:

    union Data {int intValue;double doubleValue;};Data d;d.intValue = 10;cout<<d.doubleValue; //类型不安全,存入int,取出double

但variant你做不到这样:

    std::variant<int, double> v;v = 1;cout << get<1>(v)<<endl;

编译没问题,但运行报异常:

terminate called after throwing an instance of ‘std::bad_variant_access’
what(): std::get: wrong index for variant

这是因为当前存储了什么类型是被_M_index记了下来的,在我们的例子中存了int,故_M_index=0, 而double是下一个类型其_M_index=1. 实现代码如下:

1672   template<size_t _Np, typename... _Types>
1673     constexpr variant_alternative_t<_Np, variant<_Types...>>&
1674     get(variant<_Types...>& __v)
1675     {
1676       static_assert(_Np < sizeof...(_Types),
1677             "The index must be in [0, number of alternatives)");
1678       if (__v.index() != _Np) //index()返回_M_index
1679     __throw_bad_variant_access(__v.valueless_by_exception()); //本例触发异常
1680       return __detail::__variant::__get<_Np>(__v);
1681     }

当然,类型安全的代价就是需要比union多点内存存_M_index。它的类型可能是char, 也可能是short, 这取决于你的variant声明时要容纳的类型个数:

401   template <typename... _Types>402     using __select_index =403       typename __select_int::_Select_int_base<sizeof...(_Types),404                           unsigned char,405                           unsigned short>::type::value_type;
446       _Variadic_union<_Types...> _M_u;447       using __index_type = __select_index<_Types...>;448       __index_type _M_index;449     };

3.2 默认持有第一个类型的值

a default-constructed variant holds a value of its first alternative, unless that alternative is not default-constructible
我们先通过一个例子有个直观的认识:

  1 #include <iostream>2 #include <variant>3 #include <string>4 using namespace std;56 int main() {7     class Person{8         public:9         Person(){10             char ch;11             std::cout << "Enter a character: ";12             std::cin.get(ch);13         }14     };15     // Define a variant with 2 alternatives: Person, int16     std::variant<Person,int> vpi; //并没有显示指明存入一个Person, 而实际却是存入了Person

我让程序卡在输入那(第12行),方便用GDB看下调用栈
在这里插入图片描述
关键代码在栈第14、13层:

 704       constexpr705       _Variant_base()706       noexcept(_Traits<_Types...>::_S_nothrow_default_ctor)707       : _Variant_base(in_place_index<0>) { }  //类型列表中的第一个类型即Person

**思考:**如果第一个类型没有默认构造函数哪?
答案也在文档中

unless that alternative is not default-constructible

把上面的默认构造函数置为delete, 编译出错:
在这里插入图片描述
还记得上节preview的图吗?继承_Enable_default_constructor的原因也在这里(有机会再细讲)

3.3 内存开始即分配好,没有动态分配内存

As with unions, if a variant holds a value of some object type T, the object representation of T is allocated directly within the object representation of the variant itself. Variant is not allowed to allocate additional (dynamic) memory.
这一点我们上一节已经提到过,不在赘述。

3.4 不能存入引用,数组,void

A variant is not permitted to hold references, arrays, or the type void.
实现代码有如下片段:

1346       static_assert(sizeof...(_Types) > 0,
1347             "variant must have at least one alternative");
1348       static_assert(!(std::is_reference_v<_Types> || ...),
1349             "variant must have no reference alternative");
1350       static_assert(!(std::is_void_v<_Types> || ...),
1351             "variant must have no void alternative");

显然引用、void已经被禁。而原生数组不是完整类型,不能在标准库容器中被用于模板参数。

3.5 可以重复持有相同类型

A variant is permitted to hold the same type more than once, and to hold differently cv-qualified versions of the same type.
可以持有多个相同类型,比如两个int

  1 #include <iostream>2 #include <variant>3 #include <string>4 using namespace std;56 int main() {7     std::variant<int,int> v2i;8     v2i.emplace<0>(1);9     //cout<<get<1>(v2i)<<endl; //虽然类型相同,但依然不能按第二个int取值10     v2i.emplace<1>(2);11     cout<<get<1>(v2i)<<endl;

3.6 get by type

这一条在get部分
在这里插入图片描述
获得数据不仅仅能用下标,还能用类型,比如

    std::variant<int, double> v;v = 1;cout << get<double>(v)<<endl;

后台还是找到double的下标取得数据。如何转的哪?先不要急,让我们先看看3.4中提到的例子

  7     std::variant<int,int> v2i;8     v2i.emplace<0>(1);9     //cout<<get<1>(v2i)<<endl; //虽然类型相同,但依然不能按第二个int取值10     v2i.emplace<1>(2);11     cout<<get<int>(v2i)<<endl; //get<n> 改为get<int>

这会导致编译出错,
在这里插入图片描述
显然它区分不出来你要去第几个int, 它也不允许这么用:

1116   template<typename _Tp, typename... _Types>
1117     constexpr _Tp& get(variant<_Types...>& __v)
1118     {
1119       static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
1120             "T must occur exactly once in alternatives");
1121       static_assert(!is_void_v<_Tp>, "_Tp must not be void");
1122       return std::get<__detail::__variant::__index_of_v<_Tp, _Types...>>(__v);
1123     }

我们例子中_Tp=int, _Types={int,int}, _Tp在_Types中出现了两次,导致__exactly_once是false, 所以报了1119行的T must occur exactly once in alternatives
__exactly_once的实现又是一个递归哈。

 721   // For how many times does _Tp appear in _Tuple?722   template<typename _Tp, typename _Tuple>723     struct __tuple_count;724725   template<typename _Tp, typename _Tuple>726     inline constexpr size_t __tuple_count_v =727       __tuple_count<_Tp, _Tuple>::value;728729   template<typename _Tp, typename... _Types>730     struct __tuple_count<_Tp, tuple<_Types...>>731     : integral_constant<size_t, 0> { };732733   template<typename _Tp, typename _First, typename... _Rest>734     struct __tuple_count<_Tp, tuple<_First, _Rest...>>735     : integral_constant<736     size_t,737     __tuple_count_v<_Tp, tuple<_Rest...>> + is_same_v<_Tp, _First>> { };738739   // TODO: Reuse this in <tuple> ?740   template<typename _Tp, typename... _Types>741     inline constexpr bool **__exactly_once** =742       __tuple_count_v<_Tp, tuple<_Types...>> == 1;

回到正常,如果1119行不报错,则来到

1122       return std::get<__detail::__variant::__index_of_v<_Tp, _Types...>>(__v);

查找_Tp在_Types中的下标,其实现也是递归,又是递归啊:

167   // Returns the first appearance of _Tp in _Types.168   // Returns sizeof...(_Types) if _Tp is not in _Types.169   template<typename _Tp, typename... _Types>170     struct __index_of : std::integral_constant<size_t, 0> {};171172   template<typename _Tp, typename... _Types>173     inline constexpr size_t __index_of_v = __index_of<_Tp, _Types...>::value;174175   template<typename _Tp, typename _First, typename... _Rest>176     struct __index_of<_Tp, _First, _Rest...> :177       std::integral_constant<size_t, is_same_v<_Tp, _First>178     ? 0 : __index_of_v<_Tp, _Rest...> + 1> {};

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

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

相关文章

PYTHON-使用正则表达式进行模式匹配

目录 Python 正则表达式Finding Patterns of Text Without Regular ExpressionsFinding Patterns of Text with Regular ExpressionsCreating Regex ObjectsMatching Regex ObjectsReview of Regular Expression MatchingMore Pattern Matching with Regular ExpressionsGroupi…

阿里开源低代码引擎 - Low-Code Engine

阿里开源低代码引擎 - Low-Code Engine 本文主要介绍如何在Windows运行/开发阿里开源低代码引擎 - Low-Code Engine 详细文档参见【 阿里开源低代码引擎 - Low-Code Engine 官方文档】 目录 阿里开源低代码引擎 - Low-Code Engine一、环境准备1、使用 WSL 在 Windows 上安装 L…

Java学习笔记2024/2/22

面向对象进阶部分学习方法&#xff1a; 特点&#xff1a; 逻辑性没有那么强&#xff0c;但是概念会比较多。 记忆部分重要的概念&#xff0c;理解课堂上讲解的需要大家掌握的概念&#xff0c;多多练习代码。 今日内容 复习回顾 static关键字 继承 教学目标 能够掌握st…

【开源】JAVA+Vue.js实现医院门诊预约挂号系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 功能性需求2.1.1 数据中心模块2.1.2 科室医生档案模块2.1.3 预约挂号模块2.1.4 医院时政模块 2.2 可行性分析2.2.1 可靠性2.2.2 易用性2.2.3 维护性 三、数据库设计3.1 用户表3.2 科室档案表3.3 医生档案表3.4 医生放号…

qml 保存当前界面并在其图片中添加文字

使用场景&#xff1a;在保存二维码的时候&#xff0c; 在二维码图片加标题或描述 保存后的图片 demo&#xff1a;https://download.csdn.net/download/uVarAndMethod/88868455

Electron实战之环境搭建

工欲善其事必先利其器&#xff0c;在进行实战开发的时候&#xff0c;我们最终的步骤是搞好一个舒服的开发环境&#xff0c;目前支持 Vue 的 Electron 工程化工具主要有 electron-vue、Vue CLI Plugin Electron Builder、electron-vite。 接下来我们将分别介绍基于 Vue CLI Plu…

暴雨信息|警惕AI 的变革阵痛与不稳定性

过去的未来主义幽灵使我们对数字化变革的预测保持谨慎。 我们现在经常听到&#xff0c;世界正处于一个技术转折点&#xff1b;我们正在快速步入一个由 ChatGPT 等人工智能工具塑造的未来。然而&#xff0c;我怀疑&#xff0c;2024 年我们将会被提醒到纳普斯特的幽灵——以及其他…

ChatGPT在数据分析岗位了解阶段的应用

ChatGPT在数据分析岗位了解阶段的应用 ​ 1.1 数据分析师的职责与技能要求 ​ 如果想成为数据分析师&#xff0c;首先要了解这个岗位的具体职责和技能要求。这个问题可以直接询问ChatGPT&#xff1a; ​ ChatGPT收到上述内容后&#xff0c;返回如下结果。 ​ ChatGPT给出的信…

【论文精读】Segment Anything

Segment Anything 前言Abstract1. Introduction2. Segment Anything Task3. Segment Anything Model4. Segment Anything Data Engine5. Segment Anything Dataset6. Segment Anything RAI Analysis7. Zero-Shot Transfer Experiments7.1. Zero-Shot Single Point Valid Mask E…

【开源】SpringBoot框架开发音乐平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示 四、核心代码4.1 查询单首音乐4.2 新增音乐4.3 新增音乐订单4.4 查询音乐订单4.5 新增音乐收藏 五、免责说明 一、摘要 1.1 项目介绍 基于微信小程序JAVAVueSpringBootMySQL的音乐平台&#xff0c;包含了音乐…

【ctfshow—web】——信息搜集篇1(web1~20详解)

ctfshow—web题解 web1web2web3web4web5web6web7web8web9web10web11web12web13web14web15web16web17web18web19web20 web1 题目提示 开发注释未及时删除 那就找开发注释咯&#xff0c;可以用F12来查看&#xff0c;也可以CtrlU直接查看源代码呢 就拿到flag了 web2 题目提示 j…

第3.5章:StarRocks数据导入——Broker Load

注&#xff1a;本篇文章阐述的是StarRocks-3.2版本的Broker Load导入机制 一、概述 Broker Load导入方式支持从HDFS类的外部存储系统&#xff08;例如&#xff1a;HDFS、阿里OSS、腾讯COS、华为云OBS等&#xff09;&#xff0c;支持Parquet、ORC、CSV、及 JSON 四种文件格式&a…

vue里echarts的使用:画饼图和面积折线图

vue里echarts的使用,我们要先安装echarts,然后在main.js里引入: //命令安装echarts npm i echarts//main.js里引入挂载到原型上 import echarts from echarts Vue.prototype.$echarts = echarts最终我们实现的效果如下: 头部标题这里我们封装了一个全局公共组件common-he…

qt 软件发布(Windows)

1. 开发环境 QtCreator MSVC编译器 2. 源码编译 生成release或者debug版本的exe可执行文件(x64或x86) 3. windeployqt 打包 ①左下角开始菜单栏找到QT的命令交互对话框&#xff0c;如下图MSVC 2017 64-bit(根据第二步编译的类型选择64位或者32位)。 ②cd 切换到第二步可…

TCP/IP协议详解

文章目录 TCP/IP协议概述基于TCP/IP协议的应用工具协议协议的必要性 TCP/IP协议TCP/IP协议族协议的分层 传输方式的分类报文、帧、数据包等的区别TCP 和 UDP的区别 TCP/IP协议概述 TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;是一组通信协…

《图解HTTP》笔记2:http的构成

1&#xff0c;查看浏览器上面一个具体的http请求 浏览器地址栏输入网址&#xff1a;https://news.baidu.com/ 使用浏览器的开发者工具&#xff0c;查看网络中发送和接受的数据。 可以看到输入一个网址&#xff0c;浏览器和服务器进行了很多的交互。&#xff08;绿色部分&#…

python + selenium/appnium

Selenium 的自动化原理: selenium 自动化流程: 自动化程序调用Selenium 客户端库函数&#xff08;比如点击按钮元素&#xff09;客户端库会发送Selenium 命令 给浏览器的驱动程序浏览器驱动程序接收到命令后 ,驱动浏览器去执行命令浏览器执行命令浏览器驱动程序获取命令执行的…

OJAC近屿智能张立赛博士揭秘GPT Store:技术创新、商业模式与未来趋势

> - [Look&#xff01;&#x1f440;我们的大模型商业化落地产品](https://www.airecruitas.com/aigc) >- &#x1f4d6;更多AI资讯请&#x1f449;&#x1f3fe;[关注](https://mp.weixin.qq.com/s/85qwuIydaaydMQz2g0rgMA) >- [Free三天集训营助教在线为您火热答疑…

多任务爬虫(多线程和多进程)

在一台计算机中&#xff0c;我们可以同时打开多个软件&#xff0c;例如同时浏览网页、听音乐、打字等&#xff0c;这是再正常不过的事情。但仔细想想&#xff0c;为什么计算机可以同时运行这么多软件呢? 这就涉及计算机中的两个名词&#xff1a;多进程和多线程。 同样&#xf…

通信入门系列——锁相环、平方环、Costas环

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、锁相环 1、压控振荡…