【Modern C++ Part9】Prefer-alias-declarations-to-typedefs

條款9:優先使用聲明別名而不是typedef

我有信心說,大家都同意使用STL容器是個好的想法,并且我希望,條款18可以說服你使用std::unique_ptr也是個好想法,但是我想絕對我們中間沒有人喜歡寫像這樣std::unique_ptr<std::unordered_map<std::string, std::string>>的代碼多于一次。這僅僅是考慮到這樣的代碼會增加得上“鍵盤手”的風險。

為了避免這樣的醫療悲劇,推薦使用一個typedef:

	typedefstd::unique_ptr<std::unordered_map<std::string, std::string>>UPtrMapSS;

但是typedef家族是有如此濃厚的C++98氣息。他們的確可以在C++11下工作,但是C++11也提供了聲明別名(alias declarations):

	using UptrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;

考慮到typedef和聲明別名具有完全一樣的意義,推薦其中一個而排斥另外一個的堅實技術原因是容易令人質疑的。這樣的質疑是合理的。

技術原因當然存在,但是在我提到之前。我想說的是,很多人發現使用聲明別名可以使涉及到函數指針的類型的聲明變得容易理解:

	// FP等價于一個函數指針,這個函數的參數是一個int類型和// std::string常量類型,沒有返回值typedef void (*FP)(int, const std::string&);      // typedef// 同上using FP = void (*)(int, const std::string&);     // 聲明別名

當然,上面任何形式都不是特別讓人容易下咽,并且很少有人會花費大量的時間在一個函數指針類型的標識符上,所以這很難當做選擇聲明別名而不是typedef的不可抗拒的原因。

但是,一個不可抗拒的原因是真實存在的:模板。尤其是聲明別名有可能是模板化的(這種情況下,它們被稱為模板別名(alias template)),然而typedef這是只能說句“臣妾做不到”。模板別名給C++11程序員提供了一個明確的機制來表達在C++98中需要黑客式的將typedef嵌入在模板化的struct中才能完成的東西。舉個栗子,給一個使用個性化的分配器MyAlloc的鏈接表定義一個標識符。使用別名模板,這就是小菜一碟:

	template<typname T>                             // MyAllocList<T>using MyAllocList = std::list<T, MyAlloc<T>>;   // 等同于// std::list<T,//   MyAlloc<T>>MyAllocList<Widget> lw;                         // 終端代碼

使用typedef,你不得不從草稿圖開始去做一個蛋糕:

	template<typename T>                            // MyAllocList<T>::typestruct MyAllocList {                            // 等同于typedef std::list<T, MyAlloc<T>> type;        // std::list<T, };                                              // MyAlloc<T>>MyAllocList<Widget>::type lw;                   // 終端代碼

如果你想在一個模板中使用typedef來完成創建一個節點類型可以被模板參數指定的鏈接表的任務,你必須在typedef名稱之前使用typename

	template<typename T>                            // Widget<T> 包含class Widget{                                   // 一個 MyAloocList<T>private:                                        // 作為一個數據成員typename MyAllocList<T>::type list;...};

此處,MyAllocList<T>::type表示一個依賴于模板類型參數T的類型,因此MyAllocList<T>::type是一個依賴類型(dependent type),C++中許多令人喜愛的原則中的一個就是在依賴類型的名稱之前必須冠以typename

如果MyAllocList被定義為一個聲明別名,就不需要使用typename(就像笨重的::type后綴):

	template<typname T>                             using MyAllocList = std::list<T, MyAlloc<T>>;   // 和以前一樣template<typename T>class Widget {private:MyAllocList<T> list;                         // 沒有typename...                                          // 沒有::type};

對你來說,MyAllocList<T>(使用模板別名)看上去依賴于模板參數T,正如MyAllocList<T>::type(使用內嵌的typdef)一樣,但是你不是編譯器。當編譯器處理Widget遇到MyAllocList<T>(使用模板別名),編譯器知道MyAllocList<T>是一個類型名稱,因為MyAllocList是一個模板別名:它必須是一個類型。MyAllocList<T>因此是一個非依賴類型(non-dependent type),指定符typename是不需要和不允許的。

另一方面,當編譯器在Widget模板中遇到MyAllocList<T>(使用內嵌的typename)時,編譯器并不知道它是一個類型名,因為有可能存在一個特殊化的MyAllocList,只是編譯器還沒有掃描到,在這個特殊化的MyAllocListMyAllocList<T>::type表示的并不是一個類型。這聽上去挺瘋狂的,但是不要因為這種可能性而怪罪于編譯器。是人類有可能會寫出這樣的代碼。

例如,一些被誤導的鬼魂可能會雜糅出像這樣代碼:

	class Wine {...};template<>                                 // 當T時Wine時class MyAllocList<Wine>{                   // MyAllocList 是特殊化的private:enum class WineType                      // 關于枚舉類參考條款10{ White, Red, Rose };WineType type;                           // 在這個類中,type是個數據成員...};

正如你看到的,MyAllocList<Wine>::type并不是指一個類型。如果Widget被使用Wine初始化,Widget模板中的MyAllocList<T>::type指的是一個數據成員,而不是一個類型。在Wedget模板中,MyAllocList<T>::type是否指的是一個類型忠實地依賴于傳入的T是什么,這也是編譯器堅持要求你在類型前面冠以typename的原因。

如果你曾經做過模板元編程(TMP),你會強烈地額反對使用模板類型參數并在此基礎上修改為其他類型的必要性。例如,給定一個類型T,你有可能想剝奪T所包含的所有的const或引用的修飾符,即你想將const std::string&變成std::string。你也有可能想給一個類型加上const或者將它變成一個左值引用,也就是將Widget變成const Widget或者Widget&。(如果你沒有做過TMP,這太糟糕了,因為如果你想成為一個真正牛叉的C++程序員,你至少需要對C++這方面的基本概念足夠熟悉。你可以同時看一些TMP的例子,包括我上面提到的類型轉換,還有條款23和條款27。)

C++11給你提供了工具來完成這類轉換的工作,表現的形式是type traits,它是<type_traits>中的一個模板的分類工具。在這個頭文件中有數十個類型特征,但是并不是都可以提供類型轉換,不提供轉換的也提供了意料之中的接口。給定一個你想競選類型轉換的類型T,得到的類型是std::transformation<T>::type。例如:

    std::remove_const<T>::type                 // 從 const T 得到 Tstd::remove_reference<T>::type             // 從 T& 或 T&& 得到 Tstd::add_lvalue_reference<T>::type         // 從 T 得到 T&

注釋僅僅總結了這些轉換干了什么,因此不需要太咬文嚼字。在一個項目中使用它們之前,我知道你會參考準確的技術規范。

無論如何,我在這里不是只想給你大致介紹一下類型特征。反而是因為注意到,類型轉換總是以::type作為每次使用的結尾。當你對一個模板中的類型參數(你在實際代碼中會經常用到)使用它們時,你必須在每次使用前冠以typename。這其中的原因是C++11的類型特征是通過內嵌typedef到一個模板化的struct來實現的。就是這樣的,他們就是通過使用類型同義技術來實現的,就是我一直在說服你遠不如模板別名的那個技術。

這是一個歷史遺留問題,但是我們略過不表(我打賭,這個原因真的很枯燥)。因為標準委員會姍姍來遲地意識到模板別名是一個更好的方式,對于C++11的類型轉換,委員會使這些模板也成為C++14的一部分。別名有一個統一的形式:對于C++11中的每個類型轉換std::transformation<T>::type,有一個對應的C++14的模板別名std::transformation_t。用例子來說明我的意思:

	std::remove_const<T>::type                  // C++11: const T -> Tstd::remove_const_t<T>                      // 等價的C++14std::remove_reference<T>::type              // C++11: T&/T&& -> Tstd::remove_reference_t<T>                  // 等價的C++14std::add_lvalue_reference<T>::type          // C++11: T -> T&std::add_lvalue_reference_t<T>              // 等價的C++14

C++11的結構在C++14中依然有效,但是我不知道你還有什么理由再用他們。即便你不熟悉C++14,自己寫一個模板別名也是小兒科。僅僅C++11的語言特性被要求,孩子們甚至都可以模擬一個模式,對嗎?如果你碰巧有一份C++14標準的電子拷貝,這依然很簡單,因為需要做的即使一些復制和粘貼操作。在這里,我給你開個頭:

	template<class T>using remove_const_t = typename remove_const<T>::type;template<class T>using remove_reference_t = typename remove_reference<T>::type;template<class T>using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;

看到沒有?不能再簡單了。

要記住的東西
typedef
不支持模板化,但是別名聲明支持
模板別名避免了::type
后綴,在模板中,typedef
還經常要求使用typename
前綴
C++14
C++11
中的類型特征轉換提供了模板別名

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/88650.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/88650.shtml
英文地址,請注明出處:http://en.pswp.cn/web/88650.shtml

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

相關文章

STM32第二十一天定時器TIM

1 定時器基礎知識a:上來說就是用來定時的機器&#xff0c;是存在于STM32單片機中的一個外設。STM32總共有8個定時器&#xff0c;分別是2個高級定時器(TIM1、TIM8)&#xff0c;4個通用定時器 (TIM2、TIM3、TIM4、TIM5) 和2個基本定時器 (TIM6、TIM7)&#xff0c;如下圖所示&…

鼎捷T100程序開發:校驗程序詳解

校驗程序概述 T100系統校驗程序需要確保系統數據的準確性、完整性和一致性&#xff0c;相當于企業信息系統的"健康體檢醫生"。它通過預設規則掃描系統數據&#xff0c;識別異常和錯誤&#xff0c;確保業務運行可靠。通過持續完善的校驗機制&#xff0c;企業能夠構建數…

BaseDao 通用查詢方法設計與實現

BaseDao 通用查詢方法設計與實現 一、通用查詢方法設計思路 1. 核心查詢功能矩陣查詢類型方法名功能說明復雜度主鍵查詢findById()根據主鍵獲取單個實體?全量查詢findAll()獲取全部實體?條件查詢findByCondition()動態條件查詢???分頁查詢findPage()分頁結果集????排序…

llama.cpp gguf主要量化方法

量化是一種通過降低模型參數的表示精度來減少模型的大小和計算存儲需求的方法&#xff0c;如把單精度fp32轉化為int8來減少存儲和計算成本。 常見的是線性量化&#xff0c;公式 r S(q-Z)&#xff0c;將實數值r映射為量化的整數值q&#xff0c;其中縮放因子S和零點Z根據參數分…

汽車級MCU選型新方向:eVTOL垂槳控制監控芯片的替代選型技術分析

摘要&#xff1a;隨著eVTOL&#xff08;電動垂直起降航空器&#xff09;領域的蓬勃發展&#xff0c;對于高性能、高可靠性的垂槳控制監控芯片的需求日益迫切。本文旨在深入探討汽車級MCU&#xff08;微控制單元&#xff09;在這一新興領域的應用潛力&#xff0c;以國科安芯推出…

Deepoc具身智能大模型:送餐機器人如何學會“讀心術”

Deepoc具身智能大模型&#xff1a;送餐機器人如何學會“讀心術”深夜十點的商場火鍋店&#xff0c;一臺銀色機器人正穿越喧鬧的人群。當它感知到奔跑的兒童突然變向&#xff0c;驅動輪立即反向微調0.3度&#xff1b;托盤上的牛油鍋底因顧客推椅產生晃動&#xff0c;平衡系統瞬間…

學習設計模式《十七》——狀態模式

一、基礎概念 狀態模式的本質是【根據狀態來分離和選擇行為】。 狀態模式的定義&#xff1a;允許一個對象在其內部狀態改變時改變它的行為&#xff1b;對象看起來似乎修改了它的類。 認識狀態模式序號認識狀態模式說明1狀態和行為通常指的是對象實例的屬性的值&#xff1b;而行…

python的婚紗影樓管理系統

前端開發框架:vue.js 數據庫 mysql 版本不限 后端語言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 數據庫工具&#xff1a;Navicat/SQLyog等都可以 隨著婚紗…

濾波電路Multisim電路仿真實驗匯總——硬件工程師筆記

目錄 1 濾波電路基礎知識 1.1 濾波電路的分類 1.1.1 按頻率選擇性分類 1.1.2 按實現方式分類 1.2 濾波電路的設計 1.2.1 確定濾波器類型 1.2.2 計算截止頻率 1.2.3 選擇濾波階數 1.2.4 考慮元件參數 1.2.5 仿真驗證 1.3 濾波電路的應用 1.3.1 電源濾波 1.3.2 音頻…

C++隨機打亂函數:簡化源碼與原理深度剖析

文章目錄一、Fisher-Yates洗牌算法核心原理二、std::random_shuffle簡化實現與缺陷分析簡化源碼&#xff08;核心邏輯&#xff09;原理層面的致命缺陷三、std::shuffle的現代改進與實現簡化源碼&#xff08;核心邏輯&#xff09;原理層面的關鍵改進四、隨機數生成器工作原理URB…

DBeaver連接MySQL8.0報錯Public Key Retrieval is not allowed

DBeaver 鏈接本地mysql8.0服務報錯Public Key Retrieval is not allowed為什么會出現這個錯誤&#xff1f;MySQL 8.0 默認使用新的認證插件&#xff1a;caching_sha2_password某些客戶端&#xff08;比如老版本的 JDBC 驅動或配置不當的 DBeaver&#xff09;在連接時&#xff0…

SpringBoot系列—統一功能處理(攔截器)

上篇文章&#xff1a; SpringBoot系列—MyBatis-plushttps://blog.csdn.net/sniper_fandc/article/details/148979284?fromshareblogdetail&sharetypeblogdetail&sharerId148979284&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目錄 1 攔…

《匯編語言:基于X86處理器》第7章 整數運算(3)

本章將介紹匯編語言最大的優勢之一:基本的二進制移位和循環移位技術。實際上&#xff0c;位操作是計算機圖形學、數據加密和硬件控制的固有部分。實現位操作的指令是功能強大的工具&#xff0c;但是高級語言只能實現其中的一部分&#xff0c;并且由于高級語言要求與平臺無關&am…

應用筆記|數字化儀在醫學SS-OCT中的應用

引言近些年來&#xff0c;OCT&#xff08;光學相干斷層掃描&#xff0c;Optical Coherence Tomography&#xff09;作為一種非破壞性3D光學成像技術逐漸在醫學眼科設備中流行起來。OCT可提供實時一維深度或二維截面或三維立體的圖像&#xff0c;分辨率可達微米&#xff08;μm&…

Ubuntu 22.04與24.04 LTS版本對比分析及2025年使用建議

Ubuntu 22.04與24.04 LTS版本對比分析及2025年使用建議 在2025年的技術環境下&#xff0c;Ubuntu 22.04和24.04 LTS各有優勢&#xff0c;選擇哪一個取決于具體應用場景和用戶需求。經過對系統內核、桌面環境、軟件生態、生命周期支持等多方面因素的綜合分析&#xff0c;本報告將…

Linux進程的生命周期:狀態定義、轉換與特殊場景

前言 在Linux系統中&#xff0c;進程是資源分配和調度的基本單位&#xff0c;而進程狀態則是理解進程行為的關鍵。從運行中的任務&#xff08;TASK_RUNNING&#xff09;到僵尸進程&#xff08;EXIT_ZOMBIE&#xff09;&#xff0c;每個狀態都反映了進程在內核調度、資源等待或父…

神經網絡簡介

大腦的基本計算單位是神經元&#xff08;neuron&#xff09;。人類的神經系統中大約有860億個神經元&#xff0c;它們被大約10^14-10^15個突觸&#xff08;synapses&#xff09;連接起來。下面圖表的左邊展示了一個生物學的神經元&#xff0c;右邊展示了一個常用的數學模型。每…

多路由協議融合與網絡服務配置實驗(電視機實驗)

多路由協議融合與網絡服務配置實驗文檔 一、實驗用途和意義 &#xff08;一&#xff09;用途 本實驗模擬企業復雜網絡環境&#xff0c;整合 OSPF、RIPv2 動態路由協議&#xff0c;結合 DHCP、FTP、Telnet 服務配置及訪問控制策略&#xff0c;實現多區域網絡互聯、服務部署與…

在指定conda 環境里安裝 jupyter 和 python kernel的方法

在 Conda 的指定環境中安裝 Jupyter 和 Python Kernel 是一個常見操作,以下是詳細步驟,確保在指定環境中正確配置 Jupyter 和 Python Kernel: 1. 準備工作 確保已安裝 Anaconda 或 Miniconda,Conda 環境管理工具可用。確認已創建或計劃使用的 Conda 環境。2. 步驟:安裝 J…

【數據結構與算法】數據結構初階:詳解順序表和鏈表(四)——單鏈表(下)

&#x1f525;個人主頁&#xff1a;艾莉絲努力練劍 ?專欄傳送門&#xff1a;《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題 &#x1f349;學習方向&#xff1a;C/C方向 ??人生格言&#xff1a;為天地立心&#xff0c;為生民立命&#xff0c;為…