跳转到内容

C++17 折叠表达式

来自代码酷

C++17折叠表达式[编辑 | 编辑源代码]

折叠表达式(Fold Expressions)是C++17引入的一项新特性,它允许对参数包(parameter pack)中的元素进行简洁的二元操作,从而简化可变参数模板(variadic templates)的编写。这一特性极大地提升了模板元编程的便利性,尤其在处理可变数量参数时。

基本概念[编辑 | 编辑源代码]

折叠表达式的基本思想是将一个二元操作符(如+*&&等)应用于参数包中的所有元素,最终得到一个计算结果。折叠表达式可以用于以下四种形式:

1. 一元左折叠(Unary Left Fold):(... op pack) 2. 一元右折叠(Unary Right Fold):(pack op ...) 3. 二元左折叠(Binary Left Fold):(init op ... op pack) 4. 二元右折叠(Binary Right Fold):(pack op ... op init)

其中:

  • op 是二元操作符(如+*&&等)。
  • pack 是参数包(parameter pack)。
  • init 是初始值(仅适用于二元折叠)。

一元左折叠示例[编辑 | 编辑源代码]

以下代码展示了一元左折叠的用法,计算参数包中所有元素的和:

#include <iostream>

template<typename... Args>
auto sum(Args... args) {
    return (... + args); // 一元左折叠:(args1 + args2 + ... + argsN)
}

int main() {
    std::cout << sum(1, 2, 3, 4, 5) << std::endl; // 输出:15
    return 0;
}

输出:

15

解释:

  • (... + args) 展开为 1 + 2 + 3 + 4 + 5
  • 最终结果为 15

一元右折叠示例[编辑 | 编辑源代码]

以下代码展示了一元右折叠的用法,计算参数包中所有元素的乘积:

#include <iostream>

template<typename... Args>
auto product(Args... args) {
    return (args * ...); // 一元右折叠:(args1 * args2 * ... * argsN)
}

int main() {
    std::cout << product(1, 2, 3, 4, 5) << std::endl; // 输出:120
    return 0;
}

输出:

120

解释:

  • (args * ...) 展开为 1 * 2 * 3 * 4 * 5
  • 最终结果为 120

二元折叠表达式[编辑 | 编辑源代码]

二元折叠表达式允许指定一个初始值(init),并在此基础上对参数包进行折叠操作。

二元左折叠示例[编辑 | 编辑源代码]

以下代码展示了二元左折叠的用法,计算参数包中所有元素的和,并加上初始值:

#include <iostream>

template<typename... Args>
auto sum_with_init(int init, Args... args) {
    return (init + ... + args); // 二元左折叠:(init + args1 + args2 + ... + argsN)
}

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

输出:

25

解释:

  • (init + ... + args) 展开为 10 + 1 + 2 + 3 + 4 + 5
  • 最终结果为 25

二元右折叠示例[编辑 | 编辑源代码]

以下代码展示了二元右折叠的用法,计算参数包中所有元素的乘积,并乘以初始值:

#include <iostream>

template<typename... Args>
auto product_with_init(int init, Args... args) {
    return (args * ... * init); // 二元右折叠:(args1 * args2 * ... * argsN * init)
}

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

输出:

1200

解释:

  • (args * ... * init) 展开为 1 * 2 * 3 * 4 * 5 * 10
  • 最终结果为 1200

支持的操作符[编辑 | 编辑源代码]

折叠表达式支持几乎所有二元操作符,包括:

  • 算术操作符:+-*/%
  • 逻辑操作符:&&||
  • 位操作符:&|^
  • 比较操作符:<>==!=
  • 逗号操作符:,

逻辑操作符示例[编辑 | 编辑源代码]

以下代码展示了逻辑操作符的折叠用法,检查所有参数是否为真:

#include <iostream>

template<typename... Args>
bool all_true(Args... args) {
    return (... && args); // 一元左折叠:(args1 && args2 && ... && argsN)
}

int main() {
    std::cout << std::boolalpha << all_true(true, true, false, true) << std::endl; // 输出:false
    std::cout << std::boolalpha << all_true(true, true, true) << std::endl;         // 输出:true
    return 0;
}

输出:

false
true

解释:

  • 第一个调用中,(... && args) 展开为 true && true && false && true,结果为 false
  • 第二个调用中,展开为 true && true && true,结果为 true

实际应用场景[编辑 | 编辑源代码]

折叠表达式在模板元编程中非常有用,以下是几个实际应用场景:

打印所有参数[编辑 | 编辑源代码]

以下代码使用折叠表达式和逗号操作符打印所有参数:

#include <iostream>

template<typename... Args>
void print_all(Args... args) {
    (std::cout << ... << args) << std::endl; // 二元左折叠:(cout << args1 << args2 << ... << argsN)
}

int main() {
    print_all(1, " hello ", 3.14, " world"); // 输出:1 hello 3.14 world
    return 0;
}

输出:

1 hello 3.14 world

解释:

  • (std::cout << ... << args) 展开为 std::cout << 1 << " hello " << 3.14 << " world"
  • 最终输出为 1 hello 3.14 world

检查参数是否有序[编辑 | 编辑源代码]

以下代码使用折叠表达式检查参数是否按升序排列:

#include <iostream>

template<typename... Args>
bool is_sorted(Args... args) {
    return (... < args); // 一元左折叠:(args1 < args2 < ... < argsN)
}

int main() {
    std::cout << std::boolalpha << is_sorted(1, 2, 3, 4) << std::endl;   // 输出:true
    std::cout << std::boolalpha << is_sorted(1, 3, 2, 4) << std::endl;   // 输出:false
    return 0;
}

输出:

true
false

解释:

  • 第一个调用中,(... < args) 展开为 1 < 2 < 3 < 4,结果为 true
  • 第二个调用中,展开为 1 < 3 < 2 < 4,由于 3 < 2false,最终结果为 false

总结[编辑 | 编辑源代码]

折叠表达式是C++17中一项强大的特性,它简化了可变参数模板的编写,使代码更加简洁和易读。通过一元或二元折叠,可以轻松实现参数包的聚合操作(如求和、求积、逻辑判断等)。在实际开发中,折叠表达式常用于模板元编程、泛型编程和函数式编程场景。