C++11的可變參數模板

可變參數模板函數

一個可變參數模板函數的定義如下:

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>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章