在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}

  1. 第一次调用
    T=int,Args = {const char*,double},rest... = {"hello",3.14}
    cout<<123<<endl;
    myPrint("hello",3.14);
  2. 第二次调用
    T = const char*,Args = {double},rest... ={3.14}
    cout<<"hello"<<endl;
    myPrint(3.14);
  3. 第三次调用
    T = double,Args = {},rest... ={}
    cout<<3.14<<endl;
    myPrint();
  4. 第四次调用
    cout << "the last one";

    底层机制

    递归展开在编译期间生成多个函数实现:

  5. myPrint(int,const char*,double)
  6. myPrint(const char*,double)
  7. myPrint(double)
  8. 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; }

    在下一篇文章中介绍折叠表达式

最后修改:2025 年 02 月 28 日
如果觉得我的文章对你有用,请随意赞赏