C++11的可变参数模板 (Variadic Templates) 是如何工作的?如何使用递归解包一个参数包 (parameter pack)?

C++11的可变参数模板 (Variadic Templates) 是如何工作的?如何使用递归解包一个参数包 (parameter pack)?

什么是可变参数模板?

核心语法是省略号 ...

  1. 模板参数包 (Template Parameter Pack):用于表示零个或多个模板参数

    c++ 复制代码
    template<typename... Args> // Args 是一个模板参数包
  2. 函数参数包 (Function Parameter Pack):用于表示零个或多个函数参数

    c++ 复制代码
    void my_func(Args... args); // args 是一个函数参数包
  3. 一个可以接受任何参数的函数模板可以这样声明:

    c++ 复制代码
    template<typename... Args>
    void print(Args... args) {
        // 如何处理 args... ?
    }

    可以用sizeof...(Args)sizeof...(args)来获取参数包中的参数数量


如何使用参数包?

参数包不像一个普通的容器,不能 在C++11中直接用下标(如 args[0])来访问其中的元素。必须通过一种称为**"包展开" (Pack Expansion)** 的模式来使用它

包展开就是用 ... 将参数包"解开",并应用到某个模式上。最经典、最基础的解包方式就是递归

递归解包的思路是:每次处理参数包中的第一个元素,然后用剩余的元素递归调用自身,直到参数包为空

  1. 一个递归终止函数(基础情况),用于处理参数包为空时的调用
  2. 一个可变参数模板函数(递归步骤),用于处理参数包中至少有一个参数的情况
c++ 复制代码
#include <iostream>

// 1. 递归终止函数 (Base Case)
// 当参数包为空时,这个版本的print()会被调用,递归结束。
void print() {
    std::cout << std::endl;
}

// 2. 可变参数模板函数 (Recursive Step)
template<typename T, typename... Args>
void print(T first, Args... rest) {
    // (A) 处理当前第一个参数
    std::cout << first << " ";
    
    // (B) 将剩余的参数包(rest)展开,递归调用print函数
    print(rest...); 
}

int main() {
    std::cout << "Calling print with multiple arguments:\n";
    print(10, 3.14, "C++11", 'a');

    std::cout << "\nCalling print with a single argument:\n";
    print("Hello");

    std::cout << "\nCalling print with no arguments:\n";
    print(); // 直接调用基础版本
}

/*
Calling print with multiple arguments:
10 3.14 C++11 a 

Calling print with a single argument:
Hello 

Calling print with no arguments:
*/

递归调用过程分析:

调用 print(10, 3.14, "C++11", 'a') 时,实际发生了一系列调用:

bash 复制代码
print(10, 3.14, "C++11", 'a')
  • Tintfirst10
  • Args...double, const char*, charrest...3.14, "C++11", 'a'
  • 打印 10,然后调用 print(3.14, "C++11", 'a')
bash 复制代码
print(3.14, "C++11", 'a')
  • Tdoublefirst3.14
  • Args...const char*, charrest..."C++11", 'a'
  • 打印 3.14,然后调用 print("C++11", 'a')
bash 复制代码
print("C++11", 'a')
  • Tconst char*first"C++11"
  • Args...charrest...'a'
  • 打印 "C++11",然后调用 print('a')
bash 复制代码
print('a')
  • Tcharfirst'a'
  • Args... 是空的,rest... 也是空的。
  • 打印 'a',然后调用 print()
scss 复制代码
print()
  • 匹配到基础版本 void print(),打印一个换行符,递归结束

相关推荐
A7bert77733 分钟前
【YOLOv8pose部署至RDK X5】模型训练→转换bin→Sunrise 5部署
c++·python·深度学习·yolo·目标检测
li1670902701 小时前
第二十七章:智能指针
c语言·数据结构·c++·visual studio
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【贪心与二分判定】:数列分段 Section II
c++·算法·贪心·csp·信奥赛·二分判定·数列分段 section ii
zh_xuan2 小时前
libcurl调用https接口
c++·libcurl
就叫飞六吧2 小时前
QT写一个桌面程序exe并动态打包基本流程(c++)
开发语言·c++
蜡笔小马2 小时前
1.c++设计模式-工厂模式
c++
汉克老师2 小时前
GESP2025年3月认证C++五级( 第三部分编程题(2、原根判断))
c++·算法·模运算·gesp5级·gesp五级·原根·分解质因数
winner88813 小时前
从零吃透C++命名空间、std、#include、string、vector
java·开发语言·c++
AI进化营-智能译站3 小时前
ROS2 C++开发系列07-高效构建机器人决策逻辑,运算符与控制流实战
开发语言·c++·ai·机器人
winner88813 小时前
C++ 命名空间、虚函数、抽象类、protected 权限全套通俗易懂精讲(附与 Java 对比)
java·开发语言·c++