c++ 11 引入了可变参数模板,c++ 17 引入了折叠表达式,比 c 语言的可变参数更加简洁灵活。这篇博客总结了一些例子。
- …(省略号)用于可变参数(Variadic Arguments),它可以放在模板参数 或 函数参数的后面,表示该参数是参数包(Parameter Pack),可以接收多个值。
1. 可变参数模板
递归调用是处理可变参数模板的经典方式,即通过递归地拆解参数包。
#include <iostream>
// 基本函数(终止递归)
void print() {
std::cout << "结束" << std::endl;
}
// 可变参数模板
template <typename T, typename... Args> // 这个... 表示多种不同的变量类型
void print(T first, Args... rest) { // 这个... 表示多种不同的变量类型
std::cout << first << " "; // 先打印当前参数
print(rest...); // 递归调用,传入剩余参数, 这个... 表示多种不同的传入参数
}
int main() {
print(1, 2.5, "Hello", 'A');
// 输出: 1 2.5 Hello A 结束
}
需要有递归的终止函数,比折叠表达式麻烦点
可变参数模板不仅可以用于函数,还可以用于 类模板。
#include <iostream>
// 可变参数类模板
template <typename... Args>
class MyClass {
public:
MyClass() {
std::cout << "MyClass with " << sizeof...(Args) << " template parameters\n";
}
};
int main() {
MyClass<int, double, char> obj1; // 输出: MyClass with 3 template parameters
MyClass<> obj2; // 输出: MyClass with 0 template parameters
}
- sizeof…(Args) 为一种特殊的语法,用来 获取模板参数包中的参数个数。
2. 折叠表达式
C++17 引入了 折叠表达式(Fold Expressions),可以将参数包展开,并应用一个二元操作符来对这些参数进行“折叠”,将它们合并成一个单一的值。
折叠表达式有两种基本形式:
- 左折叠(left fold), 操作从 最左侧 开始,依次将参数包中的元素与左侧的结果结合,语法:
(... op args)
- 右折叠(right fold),操作从 最右侧 开始,依次将参数包中的元素与右侧的结果结合,语法:
(op args ...)
其中,op 表示运算符,可以是 +, -, *, /, &&, ||, <<
等。可以看出,省略号在运算符左边就是左折叠,在运算符右边就是右折叠。
#include <iostream>
template <typename... Args> auto subtractLeft(Args... args) {
return (... - args); // 左折叠,依次进行减法
}
template <typename... Args> auto subtractRight(Args... args) {
return (args - ...); // 右折叠,依次进行减法
}
int main() {
std::cout << subtractLeft(10, 3, 2, 1) << std::endl; // 输出是 4
std::cout << subtractRight(10, 3, 2, 1) << std::endl; // 输出是 8
return 0;
}
第一个函数左折叠表示 10-3-2-1=4,第二个函数右折叠表示 10-(3-(2-1))=8.
- 可以直接对可变参数包进行操作,避免递归展开。
#include <iostream>
// 使用 C++17 折叠表达式
template <typename... Args>
void print(Args... args) {
((std::cout << args << " "), ...);
std::cout << std::endl;
}
int main() {
print(1, 2.5, "Hello", 'A');
// 输出: 1 2.5 Hello A
}
对所有参数包求和:
#include <iostream>
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // 左折叠,将所有参数求和
}
int main() {
std::cout << sum(1, 2, 3, 4) << std::endl; // 输出: 10
}