深入解析 C++17 折叠表达式:左折叠 vs. 右折叠

深入解析 C++17 折叠表达式:左折叠 vs. 右折叠

编码文章call10242025-02-10 11:19:0014A+A-

折叠表达式是 C++17 引入的一种语法,专门用于简化和统一对 参数包(parameter pack) 的操作。它能够在编译时展开参数包,并对其中的每个元素应用指定的操作。这种语法既简洁又高效,非常适合处理变参模板中的逻辑。


折叠表达式的语法

折叠表达式有以下几种形式:

  1. 一元左折叠
  2. (... op pack)
  3. 这表示操作符 op 应用于参数包中的所有元素,从左到右进行计算。
  4. 一元右折叠
  5. (pack op ...)
  6. 这表示操作符 op 应用于参数包中的所有元素,从右到左进行计算。
  7. 二元左折叠
  8. (init op ... op pack)
  9. 这是带初始值的左折叠。初始值 init 会作为左侧的第一个操作数。
  10. 二元右折叠
  11. (pack op ... op init)
  12. 这是带初始值的右折叠。初始值 init 会作为右侧的最后一个操作数。

示例讲解

1. 一元左折叠

计算参数包的和:

#include 

template
auto subtract(Args... args) {
    return (... - args); // 右折叠:(((args1 - args2) - args3) - ...)
}

int main() {
    std::cout << subtract(10, 3, 2) << std::endl; // 输出 1
    return 0;
}

折叠过程为:

(10 - 3) - 2 = 5

2. 一元右折叠

类似于上例,但计算方向从右到左:

#include 

template
auto subtract(Args... args) {
    return (args - ...); // 右折叠:args1 - (args2 - (args3 - ...))
}

int main() {
    std::cout << subtract(10, 3, 2) << std::endl; // 输出 9
    return 0;
}

折叠过程为:

10 - (3 - 2) = 10 - 1 = 9

3. 二元左折叠(带初值)

语法

(init - ... - args)

计算顺序

((((init - arg1) - arg2) - arg3) - arg4)

示例

#include 

template 
auto leftFoldSubtraction(int init, Args... args) {
    return (init - ... - args);
}

int main() {
    std::cout << leftFoldSubtraction(100, 10, 5, 2) << std::endl;
}

展开计算过程

((100 - 10) - 5) - 2
= 90 - 5
= 85 - 2
= 83

输出:

83

2. 二元右折叠(带初值)

语法

(args - ... - init)

计算顺序

(arg1 - (arg2 - (arg3 - (arg4 - init))))

示例

#include 

template 
auto rightFoldSubtraction(int init, Args... args) {
    return (args - ... - init);
}

int main() {
    std::cout << rightFoldSubtraction(100, 10, 5, 2) << std::endl;
}

展开计算过程

10 - (5 - (2 - 100))
= 10 - (5 - (-98))
= 10 - (5 + 98)
= 10 - 103
= -93

输出:

-93

二者的区别总结

类型

语法

计算方式

示例 (100, 10, 5, 2)

二元左折叠

(init - ... - args)

从左到右 计算

(((100 - 10) - 5) - 2) = 83

二元右折叠

(args - ... - init)

从右到左 计算

(10 - (5 - (2 - 100))) = -93

  • 左折叠:初值在最左侧,计算顺序 从左到右
  • 右折叠:初值在最右侧,计算顺序 从右到左,括号嵌套程度更深。

常见用途

  1. 递归替代 折叠表达式消除了手动展开参数包或编写递归函数的需求。
  2. 逻辑运算 利用折叠表达式对多个布尔值进行操作:
  3. template bool all(Args... args) { return (... && args); // 所有元素为真时返回 true } template bool any(Args... args) { return (... || args); // 任意一个元素为真时返回 true } int main() { std::cout << all(true, true, false) << std::endl; // 输出 0 (false) std::cout << any(false, false, true) << std::endl; // 输出 1 (true) }
  4. 打印变长参数 利用逗号表达式实现打印:
  5. template void print(Args... args) { (..., (std::cout << args << " ")); // 左折叠逐一打印 std::cout << std::endl; } int main() { print(1, 2.5, "Hello", 'C'); // 输出:1 2.5 Hello C }
  6. 自定义操作 你可以在折叠中嵌入任意的逻辑,例如统计特定条件满足的数量:
  7. template int count_positives(Args... args) { return ((args > 0) + ...); // 左折叠统计正数个数 } int main() { std::cout << count_positives(-1, 2, 0, 5, -3) << std::endl; // 输出 2 }

优势

  1. 简洁性
  2. 取代手动展开参数包或编写递归逻辑,代码更清晰。
  3. 编译期展开
  4. 折叠表达式在编译期处理,效率高,没有运行时开销。
  5. 灵活性
  6. 支持多种操作符和逻辑,适合处理复杂的变长参数。

折叠表达式是一种非常强大、灵活的工具,尤其适合需要操作变长参数的泛型编程场景。通过掌握它,你可以简化代码、提升可读性,并最大化利用编译期优化。

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4