可变参数模板函数

一个可变参数模板函数的定义如下:

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 }

输出结果为:

C++11的可变参数模板_模板类

 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>