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