可變參數模板函數
一個可變參數模板函數的定義如下:
template <class... T>
void f(T... args)
{
cout<<sizeof...(args)<<endl; //打印變參的個數
}
f(); //0
f(1,2); //2
f(1,2.5,""); //3
1.遞歸函數方式展開參數包
通過遞歸函數展開參數包,需要提供一個參數包展開的函數和一個遞歸終止函數,遞歸終止函數正是用來終止遞歸,來看下面的例子。
1 #include <iostream> 2 using namespace std; 3 4 void print() 5 { 6 cout << "empty" << endl; 7 } 8 9 template <class T,class ...Args> 10 void print(T head,Args... rest) 11 { 12 cout << "parameter "<<head << endl; //打印變參的個數 13 print(rest...); 14 } 15 16 int main() 17 { 18 print(1, 2, 3, 4); 19 return 0; 20 }
輸出結果爲:
2.逗號表達式和初始化列表方式展開參數包
遞歸函數展開參數包是一種標準做法,也比較好理解,但也有一個缺點,就是必須有一個重載的遞歸終止函數,即必須有一個同名的終止函數來終止遞歸,這樣會感覺稍有不便。其實還有一種方法可以不通過遞歸方式來展開參數包。比如:
1 #include <iostream> 2 using namespace std; 3 4 template <class T> 5 void printarg(T t) 6 { 7 cout << t << endl; 8 } 9 10 template <class ...Args> 11 void expand(Args... args) 12 { 13 int arr[] = { (printarg(args),0)... }; 14 } 15 16 int main() 17 { 18 expand(1, 2, 3, 4); 19 return 0; 20 }
也可以打印1,2,3,4四個數字。
可變參數模板類
可變參數模板類是一個帶可變模板參數的模板類,第1章中介紹的std::tuple就是一個可變模板類,它的定義如下:
template<class... Types>
class tuple;
這個可變參數模板類可以攜帶任意類型任意個數的模板參數:
std::tuple<int> tp1=std::make_tuple(1);
std::tuple<int, double> tp2=std::std::make_tuple(1, 2.5);
std::tuple<int, double,string> tp2=std::std::make_tuple(1, 2.5,"test");
可變參數模板的模板參數個數可以爲0,所以下面的定義也是合法的:
std::tuple<> tp;
1.模板遞歸和特化方式展開參數包
可變參數模板類的展開一般需要定義2~3個類,包括類聲明和特化的模板類。如下方式定義了一個基本的可變參數模板類:
template<typename... Args>structSum;
template<typename First, typename... Rest>
struct Sum<First, Rest...>
{
enum { value = Sum<First>::value +Sum<Rest...>::value);
}
template<typename Last>struct Sum<Last>
{
enum { value = sizeof (Last)};
}
這個sum類的作用是在編譯期計算出參數包中參數類型的size之和,通過sum<int, double, short>::value就可以獲取這3個類型的size之和爲14。這是一個簡單的通過可變參數模板類計算的例子,可以看到一個基本的可變參數模板應用類由三部分組成,第一部分是:
template<typename... Args> struct sum
它是前向聲明,聲明這個類是一個可變參數模板類;第二部分是類的定義:
template<typename First, typename... Rest>
struct sum<First, Rest...>
{
enum { value = Sum<First>::value +Sum<Rest...>::value);
}
它定義了一個部分展開的可變參數模板類,告訴編譯器如何遞歸展開參數包。第三部分是特化的遞歸終止類:
template<typename Last>struct Sum<Last>
{
enum { value = sizeof (Last)};
}
上面的這種3段式的定義也可以改爲兩段式,去掉前向聲明:
template<typename First, typename... Rest>
struct sum
{
enum { value = Sum<First>::vlue+Sum< Rest...>::value);
};
template<typename Last>
{
enum { value = sizeof(Last); };
}
2.繼承方式展開參數包
除了通過模板遞歸和模板特化的方式展開,還有另外一種方式:通過繼承和特化的方式展開。下面的例子就是通過繼承的方式去展開參數包
1 //整數序列的定義 2 template<int...> 3 struct IndexSeq{}; 4 5 //繼承方式,開始展開參數包 6 template<int N, int... Indexes> 7 struct MakeIndexes : MakeIndexes<N-1, N-1, Indexes...> {}; 8 9 //模板特化,終止展開參數包的條件 10 template<int... Indexes> 11 struct MakeIndexes<0, Indexes...> 12 { 13 typedef IndexSeq<Indexes...> type; 14 }; 15 16 int main() 17 { 18 using T = MakeIndexes<3>::type; 19 cout<<typeid(T).name()<<endl; 20 return 0; 21 }
最終輸出的類型是struct IndexSeq<0, 1, 2>