中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++可變參數模板的展開方式是什么

發布時間:2022-04-06 15:16:11 來源:億速云 閱讀:159 作者:iii 欄目:開發技術

這篇文章主要講解了“C++可變參數模板的展開方式是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C++可變參數模板的展開方式是什么”吧!

可變參數模板(variadic templates)是C++11新增的強大的特性之一,它對模板參數進行了高度泛化,能表示0到任意個數、任意類型的參數。相比C++98/03這些類模版和函數模版中只能含固定數量模版參數的“老古董”,可變模版參數無疑是一個巨大的進步。

如果是剛接觸可變參數模板可能會覺得比較抽象,使用起來會不太順手,使用可變參數模板時通常離不開模板參數的展開,所以本文來列舉一些常用的模板展開方式,幫助我們來對可變參數模板有一個初步的了解。

可變參數模板的定義

可變參數模板和普通模板的定義類似,在寫法上需要在 typenameclass 后面帶上省略號...,以下為一個常見的可變參數函數模板:

template <class... T>void func(T... args){    //...}

上面這個函數模板的參數 args 前面有省略號,所以它就是一個被稱為模板參數包(template parameter pack)的可變模版參數,它里面包含了0到N個模版參數,而我們是無法直接獲取 args 中的每個參數的,只能通過展開參數包的方式來獲取參數包中的每個參數,這也是本文要重點總結的內容。

參數包的展開

參數包展開的方式隨著c++語言的發展也在與時俱進,我們以實現一個可變參格式化打印函數為例,列舉一些常用的方式:

遞歸函數方式展開

#include <iostream>void FormatPrint(){    std::cout << std::endl;}template <class T, class ...Args>void FormatPrint(T first, Args... args){   std::cout << "[" << first << "]";   FormatPrint(args...);}int main(void){   FormatPrint(1, 2, 3, 4);   FormatPrint("good", 2, "hello", 4, 110);   return 0;}

這種遞歸展開的方式與遞歸函數的定義是一樣的,需要遞歸出口和不斷調用自身,仔細看看這個函數模板是不是都滿足啦?遞歸出口就是這個無模板參數的 FormatPrint,并且在有參模板中一直在調用自身,遞歸調用的過程時這樣的 FormatPrint(4,3,2,1) -> FormatPrint(3,2,1) -> FormatPrint(2,1) -> FormatPrint(1) -> FormatPrint(),輸出內容如下:

>albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testtemplate.cpp --std=c++11albert@home-pc:/mnt/d/data/cpp/testtemplate$ ./a.out[1][2][3][4][good][2][hello][4][110]

逗號表達式展開

#include <iostream>template <class ...Args>void FormatPrint(Args... args){   (void)std::initializer_list<int>{ (std::cout << "[" << args << "]", 0)... };   std::cout << std::endl;}int main(void){   FormatPrint(1, 2, 3, 4);   FormatPrint("good", 2, "hello", 4, 110);   return 0;}

這種方式用到了C++11的新特性初始化列表(Initializer lists)以及很傳統的逗號表達式,我們知道逗號表達式的優先級最低,(a, b) 這個表達式的值就是 b,那么上述代碼中(std::cout << "[" << args << "]", 0)這個表達式的值就是0,初始化列表保證其中的內容從左往右執行,args參數包會被逐步展開,表達式前的(void)是為了防止變量未使用的警告,運行過后我們就得到了一個N個元素為0的初始化列表,內容也被格式化輸出了:

albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testtemplate.cpp --std=c++11albert@home-pc:/mnt/d/data/cpp/testtemplate$ ./a.out[1][2][3][4][good][2][hello][4][110]

說到這順便提一下,可以使用sizeof...(args)得到參數包中參數個數。

enable_if方式展開

#include <iostream>#include <tuple>#include <type_traits>template<std::size_t k = 0, typename tup>typename std::enable_if<k == std::tuple_size<tup>::value>::type FormatTuple(const tup& t){    std::cout << std::endl;}template<std::size_t k = 0, typename tup>typename std::enable_if<k < std::tuple_size<tup>::value>::type FormatTuple(const tup& t){    std::cout << "[" << std::get<k>(t) << "]";    FormatTuple<k + 1>(t);}template<typename... Args>void FormatPrint(Args... args){    FormatTuple(std::make_tuple(args...));}int main(void){   FormatPrint(1, 2, 3, 4);   FormatPrint("good", 2, "hello", 4, 110);   return 0;}

C++11的enable_if常用于構建需要根據不同的類型的條件實例化不同模板的時候。顧名思義,當滿足條件時類型有效。可作為選擇類型的小工具,其廣泛的應用在 C++ 的模板元編程(meta programming)之中,利用的就是SFINAE原則,英文全稱為Substitution failure is not an error,意思就是匹配失敗不是錯誤,假如有一個特化會導致編譯時錯誤,只要還有別的選擇,那么就無視這個特化錯誤而去選擇另外的實現,這里的特化概念不再展開,感興趣可以自行了解,后續可以單獨總結一下。

在上面的代碼實現中,基本思路是先將可變模版參數轉換為std::tuple,然后通過遞增參數的索引來選擇恰當的FormatTuple函數,當參數的索引小于tuple元素個數時,會不斷取出當前索引位置的參數并輸出,當參數索引等于總的參數個數時調用另一個模板重載函數終止遞歸,編譯運行輸入以下內容:

albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testtemplate.cpp --std=c++11albert@home-pc:/mnt/d/data/cpp/testtemplate$ ./a.out[1][2][3][4][good][2][hello][4][110]

折疊表達式展開(c++17)

#include <iostream>template<typename... Args>void FormatPrint(Args... args){    (std::cout << ... << args) << std::endl;}int main(void){   FormatPrint(1, 2, 3, 4);   FormatPrint("good", 2, "hello", 4, 110);   return 0;}

折疊表達式(Fold Expressions)是C++17新引進的語法特性,使用折疊表達式可以簡化對C++11中引入的參數包的處理,可以在某些情況下避免使用遞歸,更加方便的展開參數,如上述代碼中展示的這樣可以方便的展開參數包,不過輸出的內容和之前的有些不一樣:

albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testtemplate.cpp --std=c++17albert@home-pc:/mnt/d/data/cpp/testtemplate$ ./a.out1234good2hello4110

對比結果發現缺少了格式化的信息,需要以輔助函數的方式來格式化:

#include <iostream>template<typename T>string format(T t) {    std::stringstream ss;    ss << "[" << t << "]";    return ss.str();}template<typename... Args>void FormatPrint(Args... args){    (std::cout << ... << format(args)) << std::endl;}int main(void){   FormatPrint(1, 2, 3, 4);   FormatPrint("good", 2, "hello", 4, 110);   return 0;}

這次格式化內容就被加進來了:

albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testtemplate.cpp --std=c++17albert@home-pc:/mnt/d/data/cpp/testtemplate$ ./a.out[1][2][3][4][good][2][hello][4][110]

這樣好像還是有點麻煩,我們可以把折疊表達式和逗號表達式組合使用,這樣得到的代碼就簡單多啦,也能完成格式化輸出的任務:

#include <iostream>template<typename... Args>void FormatPrint(Args... args){    (std::cout << ... << (std::cout << "[" << args, "]")) << std::endl;}int main(void){   FormatPrint(1, 2, 3, 4);   FormatPrint("good", 2, "hello", 4, 110);   return 0;}

感謝各位的閱讀,以上就是“C++可變參數模板的展開方式是什么”的內容了,經過本文的學習后,相信大家對C++可變參數模板的展開方式是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

沙洋县| 鸡西市| 宁德市| 抚顺县| 兖州市| 亚东县| 神农架林区| 离岛区| 兰考县| 香河县| 荥经县| 玉山县| 集安市| 永年县| 双流县| 唐河县| 迭部县| 临清市| 天气| 饶平县| 苏尼特左旗| 连南| 莱阳市| 华宁县| 永和县| 仁寿县| 大石桥市| 菏泽市| 河津市| 中阳县| 高邮市| 临猗县| 融水| 女性| 黑龙江省| 普陀区| 高尔夫| 甘谷县| 仙居县| 沧源| 蓬莱市|