在C++11中引入了可变参数模板,顾名思义参数的数量是可以改变的,我们先来看一段代码
#include <iostream>
using namespace std;
void myPrint()
{
cout << "the last one";
}
template <typename T, typename... Args>
void myPrint(T first, Args... rest)
{
cout << "process:" << first << endl;
myPrint(rest...);
}
int main()
{
myPrint(123, "hello", 3.14);
return 0;
}
输出
process:123
process:hello
process:3.14
the last one
我们慢慢分析这段代码,这段代码先是定义了一个myPrint函数,这个函数没有参数,当参数包为空时会调用该函数。
再是定义了一个函数模板,函数模板,模板参数中有一个typename... Args,这个参数是可变参数模板的关键。
在C++17之前可变参数模板是通过递归展开的。我们可以看到myPrint函数中调用了myPrint函数,这表明该可变参数函数模板通过递归的方式展开参数,而上述提到的无参数的函数模板就是该递归的终止条件。
对于递归展开的可变参数函数模板,必须确保每次递归参数包严格减少,必须定义终止函数即无参数同名函数。
上述代码的调用过程分析。
在main函数中我们给myPrint函数传入参数包{123,"hello",3.14}
- 第一次调用
T=int,Args = {const char*,double},rest... = {"hello",3.14}
cout<<123<<endl;
myPrint("hello",3.14); - 第二次调用
T = const char*,Args = {double},rest... ={3.14}
cout<<"hello"<<endl;
myPrint(3.14); - 第三次调用
T = double,Args = {},rest... ={}
cout<<3.14<<endl;
myPrint(); 第四次调用
cout << "the last one";底层机制
递归展开在编译期间生成多个函数实现:
- myPrint(int,const char*,double)
- myPrint(const char*,double)
- myPrint(double)
myPrint()
每个实例处理不同数量的类型。缺点
参数包过大会导致编译时间加长,生成代码体积变大。
其他方案
在C++17以后可以使用折叠表达式简化代码,避免了现实递归。
#include <iostream> using namespace std; template <typename... Args> void myPrint(Args... rest) { (cout <<...<<rest); } int main() { myPrint(123, "hello", 3.14);//输出123hello3.14 return 0; }
在下一篇文章中介绍折叠表达式