五、可变参数模版
1. 基本语法及原理
C++11 正式引入了可变参数模板,这意味着函数模板和类模板现在可以接受任意数量的参数。这些参数被称为'参数包',主要分为两类:模板参数包(零个或多个类型)和函数参数包(零个或多个形参)。
在定义时,我们使用省略号 ... 来标识一个包。例如在模板参数列表中,class... 或 typename... 表示后续是零或多个类型;在函数参数列表中,类型名后跟 ... 则表示零或多个对象。函数参数包同样支持左值引用或右值引用,实例化时遵循标准的引用折叠规则。
其底层原理其实还是模板实例化,编译器会根据传入的参数数量生成对应个数的函数版本。我们可以利用 sizeof... 运算符轻松获取参数包中的元素个数。
template <class ...Args> void Print(Args&&... args) {
cout << sizeof...(args) << endl;
}
int main() {
double x = 2.2;
Print(); // 包里有 0 个参数
Print(1); // 包里有 1 个参数
Print(1, string("xxxxx")); // 包里有 2 个参数
Print(1.1, string("xxxxx"), x); // 包里有 3 个参数
return 0;
}
从编译角度看,这本质上会结合引用折叠规则实例化出多个重载函数。如果没有可变参数模板,我们需要手动编写无数个重载版本才能支持这种功能。有了它,我们在类型泛化的基础上叠加了数量变化的能力,让泛型编程更加灵活。
2. 包扩展
对于参数包,除了计算个数,最核心的操作就是'扩展'。扩展意味着将包分解为单个元素,并对每个元素应用某种模式。通过在模式的右侧添加省略号 ... 即可触发扩展。
C++ 也支持更复杂的扩展,直接将参数包依次展开作为实参传递给函数处理。但要注意,不能像下面这样在运行时尝试访问参数包:
// 错误示范:运行时无法访问编译期参数包
template <class ...Args> {
cout << ...(args) << endl;
( i = ; i < ...(args); i++) {
cout << args[i] << ;
}
cout << endl;
}


