先看一个可变参数模板示例:
using namespace std;
namespace _nmsp1
{
void myptfunct()
{
}
//template <typename... T>
//void myvtfunct(T... args) //T:一包类型 ,args:一包形参
//{
// cout << "---------begin------------" << endl;
// cout << sizeof...(args) << endl; //收到的参数数量
// cout << sizeof...(T) << endl; //收到的类型数量
// cout << "---------end------------" << endl;
// myvtfunct(args...)
//}
//void myvtfunct()//这是个普通函数,而不是函数模板
//{
// cout << "参数包展开时执行了递归终止函数myvtfunct()" << endl;
//}
void testfunc() {}
template <typename T, typename...U>
void myvtfunct(T firstarg, U ...otherargs)
{
cout << "收到的参数值为:" << firstarg << endl;
//myvtfunct(otherargs...); //递归调用,注意塞进来的是一包形参,这里...不能省略
if constexpr (sizeof...(otherargs) > 0) //constexpr必须有否则无法成功编译,圆括号中是常量表达式
{
myvtfunct(otherargs...); //递归调用,塞进来的是一包形参,这里...不能省略
}
else
{
}
}
}
1)注意template行的写法,尖括号的typename后面带3个点(代表参数包),然后才修饰T。
2)代码行void myvtfunct(T firstarg, U ...otherargs),因为U后面带了...,所以将U称为可变参数类型。看起来是一个类型名,实际上包含的是0到多个不同的类型,args一般称为一包或一堆参数,而且每个参数的类型可以各不相同。
3)代码中sizeof...(otherargs)属于固定语法,是C++11标准引入的,用于可变参函数模板的内部,用来表示收到模板参数个数或类型的数量。它后面的圆括号可以是函数模板形参(otherargs),也可以是类型模板参数(T)。
myvtfunct()这个可变参函数模板有一个问题,就是在获得了一包参数后,程序必须要把最后一包参数挨个拿到手在处理,也就是参数包展开,一般采用递归函数的方式展开参数包,为了实现用递归函数方式展开参数包,就要求在可变参函数模板的代码编写中有一个参数包展开函数和一个同名递归终止函数,通过这两个函数把参数包展开。
4)在C++17标准中增加了一个语句,叫作编译期间if语句(constexpr if),通过编译时求值,就可以将递归终止函数省下来不写。