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.pswp.cn/news/697470.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/697470.shtml
英文地址,請注明出處:http://en.pswp.cn/news/697470.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LINUX操作系統:重定向

輸出重定向&#xff1a;將命令行程序的輸出重定向到其他位置&#xff0c;如文件、程序、打印機等。 輸入重定向&#xff1a;從其他位置獲取輸入&#xff0c;而不是從標準輸入&#xff08;鍵盤、鼠標等&#xff09; 錯誤重定向&#xff1a;同輸出。 輸出重定向&#xff08;Outp…

R語言【sp】——over(),%over%

Package sp version 1.5-0 Description 點、網格和多邊形的一致空間覆蓋:在對象x的空間位置從空間對象y檢索索引或屬性。 Usage over(x, y, returnList = FALSE, fn = NULL, ...) x %over% y Arguments 參數【x】:查詢的幾何(位置)。 參數【y】:層,從中查詢幾何或屬性。…

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…

方法鑒權:基于 Spring Aop 的注解鑒權

在Spring框架中&#xff0c;可以使用面向切面編程&#xff08;AOP&#xff09;來實現注解鑒權。這通常涉及到定義一個切面&#xff08;Aspect&#xff09;&#xff0c;該切面會在方法執行前進行攔截&#xff0c;并根據注解value值來決定是否允許執行該方法。 簡單思路&#xf…

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…

一、計算機的語言奇跡:探秘ChatGPT的智能回答和寫作能力—我耀學IT

目前我們這個行業&#xff0c;最火的話題無疑是AI人工智能&#xff0c;類似ChatGPT這樣的智能Ai,今天剩下的時間不多&#xff0c;每天一個主題&#xff0c;我給大家講一下計算機回答問題和寫作的能力&#xff0c;尤其是聊天型AI模型ChatGPT。讓大家可以更加前沿的了解一下關于它…

暴雨信息|警惕AI 的變革陣痛與不穩定性

過去的未來主義幽靈使我們對數字化變革的預測保持謹慎。 我們現在經常聽到&#xff0c;世界正處于一個技術轉折點&#xff1b;我們正在快速步入一個由 ChatGPT 等人工智能工具塑造的未來。然而&#xff0c;我懷疑&#xff0c;2024 年我們將會被提醒到納普斯特的幽靈——以及其他…

ChatGPT在數據分析崗位了解階段的應用

ChatGPT在數據分析崗位了解階段的應用 ? 1.1 數據分析師的職責與技能要求 ? 如果想成為數據分析師&#xff0c;首先要了解這個崗位的具體職責和技能要求。這個問題可以直接詢問ChatGPT&#xff1a; ? ChatGPT收到上述內容后&#xff0c;返回如下結果。 ? ChatGPT給出的信…

LeetCode56.合并區間

題目 以數組 intervals 表示若干個區間的集合&#xff0c;其中單個區間為 intervals[i] [starti, endi] 。請你合并所有重疊的區間&#xff0c;并返回 一個不重疊的區間數組&#xff0c;該數組需恰好覆蓋輸入中的所有區間 。 示例 輸入&#xff1a;intervals [[1,3],[2,6]…

【論文精讀】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;包含了音樂…

Python 類型提示(Type Hinting)及typing庫

目錄 為什么要進行類型提示變量添加靜態類型注釋函數參數的類型注釋**基本類型注釋****基于`typing`庫**其他高級用法注意事項特殊情況類引用自身實例作為形參時的類型注釋參數要求為一個函數為什么要進行類型提示 從 Python 3.5 開始引入,類型提示允許程序員為變量、函數參數…

【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 切換到第二步可…