介紹C++11中的可變參數模闆和一些相關的模闆知識
目錄
概述
前言
可變模闆參數的文法
函數
類
非類型模闆參數和可變模闆參數結合
函數參數包的展開
C++11中的展開方式
遞歸展開
逗号表達式搭配initializer_list展開
C++17中的折疊表達式
一進制折疊表達式
二進制折疊表達式
如何評價
類參數包的展開
概述
大家在C++中應該見過不少函數,它們既沒有限制參數的類型,也沒有限制參數的個數,比如<code>vector<T>::emplace()</code>,<code>make_unique<T>()</code>。它們都是利用C++11中的可變模闆參數來實作的。對于這一新特性,需要掌握以下三點
可變模闆參數的文法
參數包的展開
實踐
前言
在講可變模闆參數之前,需要先講C語言中的變長參數
C語言中的<code>va_arg</code>宏并不能判斷出哪個參數參數包的末尾,是以隻能通過自己設定結束位,并通過顯式判斷來截取有效的參數
而在C++11中,使用變長參數更簡單了(好吧其實也不怎麼算變長)
以C++11的标準來看,聲明一個可變參數的模闆函數有兩種方法
函數參數包的展開
假設我們通過設計一個函數,能逐個輸出它的參數
以上代碼的遞歸過程為
<code>print(1, 2, "Mike", 3.21);</code>
<code>print(2, "Mike", 3.21);</code>
<code>print("Mike", 3.21);</code>
<code>print(3.21);</code>
<code>print();</code>

通過遞歸方式展開參數包,當所有參數包展開完畢後,自然為空,是以調用到非模闆的遞歸中止函數
當然還可以使用模闆遞歸中止函數,這種情況就不支援空參數包了
而在C++17中,對遞歸展開法進行了優化(前提是将<code>if</code>語句聲明為常量表達式)
在C++11中通過遞歸展開參數包的缺點很明顯,需要重載一個遞歸終止函數,同時還需要判定終止函數是否需要使用到模闆;不僅如此,還需要確定帶參數包版本的函數至少包含一個類型(<code>T fistst</code>),可以說是很不便了。下面介紹逗号表達式結合<code>initializer_list</code>的展開方法
這種搭配<code>initializer_list</code>的解法我願稱之為黑魔法,在構造初始化清單的同時完成了參數包的展開
以下示範一個求和模闆函數,如果我們直接使用參數包進行操作而不展開它,那麼我們将會得到報錯
先來看看如何在C++17中利用折疊表達式<code>(...)</code>展開參數包實作一個求平均數的函數
對于一進制折疊表達式而言,隻有逗号運算符,<code>&&</code>和<code>||</code>操作允許空包,其它的如果出現空包則會編譯出錯
When a unary fold is used with a pack expansion of length zero, only the following operators are allowed: Logical AND (&&). The value for the empty pack is true Logical OR (||). The value for the empty pack is false The comma operator (,). The value for the empty pack is void()
二進制折疊表達式,支援空包操作
對于<code>std::cout</code>的二進制表達式而言,隻能使用左折疊(因為輸出必須以<code>std::cout</code>開頭,而這也就代表了它是左折疊)
拓展:以下代碼是在參數包展開完畢之後再輸出<code>std::endl</code>,而不是每拆一次就輸出一次
Fold expressions with arbitrary callable?
類參數包的展開
C++11中可以使用遞歸或逗号表達式來展開函數參數包,C++17中可以使用優化的遞歸或折疊表達式來展開。
C++11中的類參數包的展開需要運用到類模闆的偏特化
此展開方式不支援0參數包,是以可以改寫為以下方式