可变模版参数(variadic templates)
是C++11新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数、任意类型的参数。
模板参数包
template<typename... A> class Car;
//typename...就表示一个模板参数包。可以这么来实例化模板:
Car<int, char> car;
包扩展
template<typename... a> class Car {};
template<typename... A> class BMW : public Car<A...> { };
BMW<int, char> car;
// A...称之为包扩展(pack extension),包扩展是可以传递的。
特性
template <class... T>
void f(T... args)
{
cout << sizeof...(args) << endl; //打印变参的个数
}
f(); //0
f(1, 2); //2
f(1, 2.5, ""); //3
可变参数模板函数展开参数包
递归函数方式展开
//递归终止函数 特化的模板函数
void print() {
cout << "empty" << endl;
}
//展开函数
template <class T, class... Args>
void print(T head, Args... rest) {
cout << "parameter " << head << endl;
print(rest...);
}
//sum例子
template<typename T>
T sum(T t)
{
return t;
}
template<typename T, typename ... Types>
T sum (T first, Types ... rest)
{
return first + sum<T>(rest...);
}
逗号表达式展开
递归函数展开参数包是一种标准做法,也比较好理解,但也有一个缺点,就是必须要一个重载的递归终止函数,即必须要有一个同名的终止函数来终止递归,这样可能会感觉稍有不便。有没有一种更简单的方式呢?其实还有一种方法可以不通过递归方式来展开参数包,这种方式需要借助逗号表达式和初始化列表。比如前面print的例子可以改成这样:
template <class T>
void print(T t){
cout << t << endl;
}
template <class ...Args>
void expand(Args... args){
int arr[] = {(print(args), 0)...};
}
expand(1,2,3,4);
//用到数组的初始化列表,这个数组的目的纯粹是为了在数组构造的过程展开参数包。
//{(printarg(args), 0)...}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... ),
//最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]。printarg便会处理参数包中每一个参数。
支持lambda表达式
template<class F, class... Args>void expand(const F& f, Args&&...args) {//这里用到了完美转发
initializer_list<int>{(f(std::forward< Args>(args)),0)...};
}
expand([](int i){cout<<i<<endl;}, 1,2,3);
可变模版参数类展开参数包
可变参数模板类的参数包展开需要通过模板特化和继承方式去展开。
模版偏特化和递归方式来展开参数包
//前向声明(限定了参数至少是1个)
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) };
};
不带声明的版本(限定了参数至少是1个)
template<typename First, typename... Rest>
struct Sum {
enum { value = Sum<First>::value + Sum<Rest...>::value };
};
template<typename Last>
struct Sum<Last> {
enum { value = sizeof(Last) };
};
继承方式展开参数包
//整型序列的定义
template<int...>
struct IndexSeq {};
//继承方式,开始展开参数包
template<int N, int... Indexes>
struct MakeIndexes : MakeIndexes<N - 1, N - 1, Indexes...> {};
// 模板特化,终止展开参数包的条件
template<int... Indexes>
struct MakeIndexes<0, Indexes...> {
typedef IndexSeq<Indexes...> type;
};
MakeIndexes<3> b;
MakeIndexes<6>::type c;
cout << typeid(b).name() << endl;//struct IndexSeq<0,1,2>
cout << typeid(c).name() << endl;//struct IndexSeq<0, 1, 2, 3, 4, 5>
/*展开过程
MakeIndexes<3> : MakeIndexes<2, 2>{}
MakeIndexes<2, 2> : MakeIndexes<1, 1, 2>{}
MakeIndexes<1, 1, 2> : MakeIndexes<0, 0, 1, 2>
MakeIndexes<0, 0, 1, 2>{
typedef IndexSeq<0, 1, 2> type;
}
*/
可变参数模版消除重复代码
消除重复代码,去掉参数个数的限制,代码很简洁
template<typename… Args>
T* Instance(Args&&… args)
{
return new T(std::forward<Args>(args)…);
}
A* pa = Instance<A>(1);
B* pb = Instance<B>(1,2);