在C++11之前,类模板和函数模板只能包含固定数量的模板参数。
在C++11之中,类模板和函数 模板允许模板定义中包含0到任意个模板参数。
声明可变参数模板时需要在typename或class后面带上省略号"...",省略号的作用:
1、声明一个参数包,包含0到任意个参数
2、在模板定义的右边,可以将参数包展开成一个个独立的参数
可变参数模板函数
可变参数模板函数的定义如下:
template<class... T>
void f(T... args)
{
cout<<sizeof...(args)<<endl;//打印变参的个数
}
展开参数包的方法有两种:
1、通过递归的模板函数将参数包展开
2、逗号表达式和初始化列表方式展开参数包
通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数。
#include <iostream>
#include <tuple>
using namespace std;
//通过std::enable_if来选择合适的重载函数打印可变模板参数,先将可变模板参数转换为tuple,然后通过递增参数的索引来选择print函数,
// 当参数的索引小于总的参数个数时,会不断取出当前索引位置的参数输出,当参数索引等于总的参数个数时终止递归
template <std::size_t I=0,typename Tuple>
typename std::enable_if<I == std::tuple_size<Tuple>::value>::type printtp(Tuple)
{
//终止递归函数
}
template <std::size_t I=0,typename Tuple>
typename std::enable_if<I < std::tuple_size<Tuple>::value>::type printtp(Tuple t)
{
std::cout<<std::get<I>(t)<<std::endl;
printtp<I+1>(t);
}
template <typename... Args>
void print(Args... args)
{
printtp(std::make_tuple(args...));
}
int main(void)
{
print(1,2,3,4);
return 0;
}
逗号表达式和初始化列表方式展开参数包
#include <iostream>
using namespace std;
template <class T>
void printarg(T t)
{
cout<<t<<endl;
}
template <typename... Args>
void expand(Args... args)
{
std::initializer_list<int>{(printarg(args), 0)...};
}
int main(void)
{
expand(1,2,3,4);
return 0;
}
可变参数模板类
一个带可变模板参数的模板类,std::tuple就是一个可变模板类
template<class... Types>
class tuple;
其展开方式与可变模板函数的展开方式不同,展开方式有两种:
1、模板递归和模板特化方式展开
2、模板继承和模板特化方式展开
模板递归和模板特化方式展开
#include <iostream>
using namespace std;
//特化方式展开模板函数,需要一个基本的模板类定义和一个特化的终止函数,而且限定了模板参数至少有一个,即不能为0
template <typename... Args>struct sum;
template <typename First,typename... Rest>
struct sum<First,Rest...> : std::integral_constant<int,sum<First>::value+sum<Rest...>::value>
{
};
template <typename Last>
struct sum<Last> : std::integral_constant<int,sizeof(Last)>
{
};
int main(void)
{
cout<<sum<int,double,long>::value<<endl;
return 0;
}
模板继承和模板特化方式展开
#include <iostream>
#include <typeinfo>
using namespace std;
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;
};
int main(void)
{
using T=MakeIndexes<3>::type;
cout<<typeid(T).name()<<endl;
return 0;
}