C++变长参数模板
C++變長參數(shù)模板
C++11 加入了新的表示方法:
允許任意個數(shù)、任意類別的模板參數(shù),同時也不需要在定義時將參數(shù)的個數(shù)固定。
模板類 Magic 的對象,能夠接受不受限制個數(shù)的 typename 作為模板的形式參數(shù),例如下面的定義:
class Magic<int,std::vector<int>,std::map<std::string,std::vector<int>>> darkMagic;既然是任意形式,所以個數(shù)為 0 的模板參數(shù)也是可以的:class Magic<> nothing;。
如果不希望產(chǎn)生的模板參數(shù)個數(shù)為 0,可以手動的定義至少一個模板參數(shù):
template<typename Require, typename... Args> class Magic;變長參數(shù)模板也能被直接調(diào)整到到模板函數(shù)上。傳統(tǒng) C 中的 printf 函數(shù),
雖然也能達(dá)成不定個數(shù)的形參的調(diào)用,但其并非類別安全。
而 C++11 除了能定義類別安全的變長參數(shù)函數(shù)外,
還可以使類似 printf 的函數(shù)能自然地處理非自帶類別的對象。
除了在模板參數(shù)中能使用 ... 表示不定長模板參數(shù)外,
函數(shù)參數(shù)也使用同樣的表示法代表不定長參數(shù),
這也就為我們簡單編寫變長參數(shù)函數(shù)提供了便捷的手段,例如:
那么我們定義了變長的模板參數(shù),如何對參數(shù)進行解包呢?
首先,我們可以使用 sizeof... 來計算參數(shù)的個數(shù),:
template<typename ...Ts> void magic(Ts... args) {cout << sizeof...(args) << endl; }1. 遞歸模板函數(shù)
遞歸是非常容易想到的一種手段,也是最經(jīng)典的處理方法。這種方法不斷遞歸地向函數(shù)傳遞模板參數(shù),進而達(dá)到遞歸遍歷所有模板參數(shù)的目的:
#include <iostream> template<typename T0> void printf1(T0 value) {std::cout << value << std::endl; } template<typename T, typename... Ts> void printf1(T value, Ts... args) {std::cout << value << std::endl;printf1(args...); } int main() {printf1(1, 2, "123", 1.1);return 0; }2. 變參模板展開
你應(yīng)該感受到了這很繁瑣,在 C++17 中增加了變參模板展開的支持,于是你可以在一個函數(shù)中完成 printf 的編寫:
template<typename T0, typename... T> void printf2(T0 t0, T... t) {std::cout << t0 << std::endl;if constexpr (sizeof...(t) > 0) printf2(t...); }事實上,有時候我們雖然使用了變參模板,卻不一定需要對參數(shù)做逐個遍歷,我們可以利用 std::bind 及完美轉(zhuǎn)發(fā)等特性實現(xiàn)對函數(shù)和參數(shù)的綁定,從而達(dá)到成功調(diào)用的目的。
3. 初始化列表展開
遞歸模板函數(shù)是一種標(biāo)準(zhǔn)的做法,但缺點顯而易見的在于必須定義一個終止遞歸的函數(shù)。
這里介紹一種使用初始化列表展開的黑魔法:
template<typename T, typename... Ts> auto printf3(T value, Ts... args) {std::cout << value << std::endl;(void) std::initializer_list<T>{([&args] {std::cout << args << std::endl;}(), value)...}; }在這個代碼中,額外使用了 C++11 中提供的初始化列表以及 Lambda 表達(dá)式的特性(下一節(jié)中將提到)。
通過初始化列表,(lambda 表達(dá)式, value)... 將會被展開。由于逗號表達(dá)式的出現(xiàn),首先會執(zhí)行前面的 lambda 表達(dá)式,完成參數(shù)的輸出。
為了避免編譯器警告,我們可以將 std::initializer_list 顯式的轉(zhuǎn)為 void。
C++ 17 中將變長參數(shù)這種特性進一步帶給了表達(dá)式,考慮下面這個例子:
#include <iostream> template<typename ... T> auto sum(T ... t) {return (t + ...); } int main() {std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl; }非類型模板參數(shù)推導(dǎo)
前面我們主要提及的是模板參數(shù)的一種形式:類型模板參數(shù)。
template <typename T, typename U>auto add(T t, U u) { return t+u;}其中模板的參數(shù) T 和 U 為具體的類型。
但還有一種常見模板參數(shù)形式可以讓不同字面量成為模板參數(shù),即非類型模板參數(shù):
在這種模板參數(shù)形式下,我們可以將 100 作為模板的參數(shù)進行傳遞。
在 C++11 引入了類型推導(dǎo)這一特性后,我們會很自然的問,既然此處的模板參數(shù)
以具體的字面量進行傳遞,能否讓編譯器輔助我們進行類型推導(dǎo),
通過使用占位符 auto 從而不再需要明確指明類型?
幸運的是,C++17 引入了這一特性,我們的確可以 auto 關(guān)鍵字,讓編譯器輔助完成具體類型的推導(dǎo),
例如:
總結(jié)
- 上一篇: ES6 Map和Set的用法笔记
- 下一篇: 软件工程理论与实践第二版吕云翔课后习题答