這次介紹另一個C++11重要的特性Variadic templates
從printf說起 相信每個寫程式的人,就算沒用過printf,也聽過printf的名字,printf的徒子徒孫大概跟Unix的子孫一樣多。而一般的printf使用方式就類似如此。
1 2 int my_printf (const char * format, ...) ;my_printf("%d + %d = %d" , a, b, a + b);
在設計一個通用函數的時候,無法知道後面參數有多少個,因此需要一個支持不定參數的機制。 從上面的程式碼看出,我們支援不定參數的語法就是...
來表示。 而在C語言如何實做這樣的機制,可以參考MSDN上的範例 。
Marco也支援不定參數 在C99標準裡,Macro支持不定參數,不過Visual Studio至今不支援C99。 我們可以寫類似這樣的Macro
1 #define dprintf(enable, ...) dprintf_impl(__FILE__, __LINE__, enable, __VA_ARGS__)
在參數列的最後面寫 …,然後就可以用 VA_ARGS 代表 … 所傳入的參數。
如何讓Template支援不定參數 從C++98談起 在實作Command Design Pattern的時候,常常需要把外部函數的參數原封不動的傳遞至內部函數,解決方法大概就像這樣。
1 2 3 4 5 6 7 8 9 10 11 12 #define P1 typename T1 #define A1 T1 &&v1 #define V1 v1 #define P2 typename T1, typename T2 #define A2 T1 &&v1, T2 &&v2 #define V2 v1, v2 #define P3 typename T1, typename T2, typename T3 #define A3 T1 &&v1, T2 &&v2, T3 &&v3 #define V3 v1, v2, v3 template <P1> void outer (A1) { inner(V1); }template <P2> void outer (A2) { inner(V2); }template <P3> void outer (A3) { inner(V3); }
這方案的缺點大概有以下幾點
可代入的參數數量有限 (雖然可以手動擴充)
程式碼難以維護,核心的程式碼不多,但是重複的程式碼很多
編譯速度緩慢
極度依賴Preprocessor 加上C++11的新特性之後,問題變得更複雜了。
C++11時期 來個最簡單的範例
1 2 3 4 5 6 7 8 9 template <typename ... Args>class VariadicTemplate { };template <typename T, typename ...Args>class VariadicTemplate1 { };VariadicTemplate<> a; VariadicTemplate<int > b; VariadicTemplate1<> c; VariadicTemplate1<double , int , string > d;
在Args左邊出現...
時,表示Args是一個Template type parameter pack ,如上面的最後一行,T就是double,而Args就是int, string,除了類別之外,非類別的template paramter也可以這樣使用。如下
1 2 3 template <unsigned ...dims>class Array { };Array<3 , 4 , 5 > arr;
而Function template也可以像Class Template一樣使用不定參數
1 2 template <typename ...Args>void func (Args ...args) ;
這裡的Args不是Type,args也不是一個value,所以以下的程式碼會出問題
1 2 3 typedef Args MyList;MyList var; auto copy = args;
而sizeof也跟著Variadic templates而新增新特性,sizeof...
可以印出Args到底有多少個參數
1 2 3 4 5 6 template <typename ... Args>struct VariadicTemplate { static const unsigned short int size = sizeof ...(Args); }; cout << VariadicTemplate<>::size << endl ; cout << VariadicTemplate<int , int , int >::size << endl ;
如何解決之前的問題 用新的特性同時解決perfect forwarding跟Variadic templates
1 2 template <typename ...Ts>void outer (Ts&& ...args) { inner(std ::forward<Ts>(args)...); }
如何抽取單一個型別與參數 透過Template specification來實作
1 2 3 4 5 6 7 8 template <typename T>void print (T &&v) ;template <typename T, typename ...Args>void print (T &&v, Args ...args) { print(std ::forward<T>(v)); print(std ::forward<Args>(args)...); }
還有其他未介紹到的特性,基於所知有限<無法完全說明,可以參考Variadic Templates (Revision 3) Draft 跟Variadic Templates are Funadic