C++11的可变参数模板 (Variadic Templates) 是如何工作的?如何使用递归解包一个参数包 (parameter pack)?
什么是可变参数模板?
核心语法是省略号 ...
-
模板参数包 (Template Parameter Pack):用于表示零个或多个模板参数
c++template<typename... Args> // Args 是一个模板参数包
-
函数参数包 (Function Parameter Pack):用于表示零个或多个函数参数
c++void my_func(Args... args); // args 是一个函数参数包
-
一个可以接受任何参数的函数模板可以这样声明:
c++template<typename... Args> void print(Args... args) { // 如何处理 args... ? }
可以用
sizeof...(Args)
或sizeof...(args)
来获取参数包中的参数数量
如何使用参数包?
参数包不像一个普通的容器,不能 在C++11中直接用下标(如 args[0]
)来访问其中的元素。必须通过一种称为**"包展开" (Pack Expansion)** 的模式来使用它
包展开就是用 ...
将参数包"解开",并应用到某个模式上。最经典、最基础的解包方式就是递归
递归解包的思路是:每次处理参数包中的第一个元素,然后用剩余的元素递归调用自身,直到参数包为空
- 一个递归终止函数(基础情况),用于处理参数包为空时的调用
- 一个可变参数模板函数(递归步骤),用于处理参数包中至少有一个参数的情况
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')
T
是int
,first
是10
。Args...
是double, const char*, char
,rest...
是3.14, "C++11", 'a'
。- 打印
10
,然后调用print(3.14, "C++11", 'a')
。
bash
print(3.14, "C++11", 'a')
T
是double
,first
是3.14
。Args...
是const char*, char
,rest...
是"C++11", 'a'
。- 打印
3.14
,然后调用print("C++11", 'a')
。
bash
print("C++11", 'a')
T
是const char*
,first
是"C++11"
。Args...
是char
,rest...
是'a'
。- 打印
"C++11"
,然后调用print('a')
。
bash
print('a')
T
是char
,first
是'a'
。Args...
是空的,rest...
也是空的。- 打印
'a'
,然后调用print()
。
scss
print()
- 匹配到基础版本
void print()
,打印一个换行符,递归结束