前言
折叠表达式是C++17中引入的一项特性,旨在简化可变参数模板的处理。它允许开发者对模板参数包中的所有元素执行一个给定的操作符,从而将它们“折叠”成单一的值。
一、基本语法
折叠表达式可分为四种类型:
- 一元右折叠: ( pack op … )
- 一元左折叠: ( … op pack )
- 二元右折叠:( pack op … op init )
- 二元左折叠: ( init op … op pack )
op是二元操作符,pack是参数包,init是初始值。
折叠表达式支持所有C++中的二元操作符,包括但不限于:
算术运算符:+, -, *, /, %
逻辑运算符:&&, ||
比较运算符:==, !=, <, >, <=, >=
位运算符:&, |, ^, <<, >>
左折叠从参数包的第一个元素开始处理,而右折叠从最后一个元素开始处理。
带初始值的折叠表达式(二元折叠)提供了额外的灵活性,允许在操作开始或结束时引入一个固定的初始值。
上述几种折叠表达式,展开后解释如下:
1) 一元右折叠 (E op …) 展开后变成:(E1 op (… op (EN-1 op EN)))
2)一元左折叠 (… op E) 展开后变成:(((E1 op E2) op …) op EN)
3)二元右折叠 (E op … op I) 展开后变成:(E1 op (… op (EN−1 op (EN op I))))
4)二元左折叠 (I op … op E) 展开后变成:((((I op E1) op E2) op …) op EN)
二、示例代码
1. 一元左折叠
template<typename... Args>
auto sum(Args... args) {
return (... + args); // 使用折叠表达式
}
2. 一元右折叠
template<typename... Args>
auto concat_right(Args... args) {
return (args + ...); // 正确的右折叠示例
}
3. 二元左折叠
#include <iostream>
template<typename T, typename... Args>
auto sum_with_init(T init, Args... args) {
return (init + ... + args);
}
int main() {
auto result = sum_with_init(10, 1, 2, 3, 4); // 初始值为10
std::cout << "The sum is: " << result << std::endl; // 应该输出20
return 0;
}
4. 二元右折叠
#include <iostream>
template<typename... Args>
auto sum_right_fold(Args... args) {
int init = 0; // 初始值
return (args + ... + init); // 使用初始值的右折叠
}
int main() {
auto result = sum_right_fold(1, 2, 3, 4); // 应该计算为 1 + (2 + (3 + (4 + 0)))
std::cout << "The sum is: " << result << std::endl;
return 0;
}
三、其他
- 使用操作符 sizeof… 获取模板参数包args中的参数个数。
例: sizeof…(args) - 二元折叠表达式中的两个op必须相同。
- 如果初始值 init 是一个包含操作符的表达式,且init中的操作符比折叠表达式中的op优先级高,则 init 要用()括起来。
例:(args + … + (1 * 2))
总结
折叠表达式是C++17标准的一个强大特性,它为模板编程提供了更高效、更简洁的方式来处理可变数量的参数。通过使用折叠表达式,开发者可以轻松实现参数包的累积、逻辑操作等复杂操作,无需依赖于复杂的模板递归或其他技巧。