Lambda 在 C++ 中提供了一种灵活而简洁的方式来编写类似函数的对象,并在现代 C++ 编程中被广泛使用。
Lambda 的定义使用以下语法:
捕获列表(capture list)用于指定 lambda 中可访问的来自外部作用域的变量。变量可以通过值捕获、引用捕获或使用 this 捕获。
- 参数列表(argument list)指定将递给 lambda 的参数。
- 返回类型(return type)指定 lambda 将返回的值的类型。如果未指定,则编译器将尝试推断其类型。
- 函数体(function body)指定 lambda 被调用时将执行的代码。
以下是在C++中使用 lambda 的几种不同方式。
1、函数回调
函数回调是将一个函数作为参数传递给另一个函数,并在接收函数稍后的时间调用该函数。您可以将 lambda 作为函数参数传递,其中它将在发生某个事件时执行。
示例:
在这个例子中,sum 变量是一个Lambda表达式,它接受两个参数 a 和 b,并返回它们的总和。std::accumulate 函数接受数字向量、结果的初始值和 sum 函数(Lambda 表达式)。该函数计算向量中所有元素的总和并返回结果,该结果被打印在屏幕上。
另一个例子:
在这种情况下,lambda 表达式 [&sum](int x) { sum += x; } 被传递作为要应用于每个元素的函数。lambda 通过引用 & 捕获变量 sum,以便可以在 lambda 主体中进行修改。
两个例子都达到了相同的结果,但第二个例子使用了 std::for_each 算法和 lambda 表达式,这是在 C++ 中更现代、更简洁的技术。
2、默认捕获
当一个 lambda 表达式被声明时没有任何显式的捕获,其默认行为是通过引用捕获周围作用域中的变量。这被称为默认捕获。
例子:
在第二个例子中,定义了一个 lambda 表达式并将其存储在名为 square 的变量中。这个 lambda 表达式接受一个 int 类型的参数 x,并返回 x * x 的值,即参数的平方。
在主函数中,这个 lambda 表达式被用作函数对象。通过传递一个值为 5 的参数来调用它,并使用 cout 流显示结果。
3、按值捕获
这是 lambda 表达式的最简单形式,其中你通过值传递变量给函数。当一个变量被按值捕获时,它的当前值被存储在闭包中,而当周围作用域中的变量发生改变时,它的值不会被更新。这可以通过将变量包含在方括号 [ ] 中来实现。
例子:
4、按引用捕获
你可以通过使用 & 符号将变量传递给 lambda 表达式来按引用捕获变量。当一个变量被按引用捕获时,它的当前值被存储在闭包中,并且在周围作用域中变量发生变化时被更新。这是通过在方括号[ ]中在变量前加上取地址运算符 & 来实现的。
例子:
5、可变Lambda表达式
默认情况下,由 lambda 表达式捕获的变量是常量,不能在 lambda 表达式体内修改。如果你想要在 lambda 表达式中修改捕获的变量,你可以将 lambda 表达式设为可变。可变lambda允许捕获的变量被修改。这是通过在方括号 [ ] 中包含可变关键字来实现的。
例子:
lambda 表达式类似于普通函数,但它们有一些关键的区别。例如,lambda 表达式的类型没有被显式指定,但可以由编译器推断出来。此外,lambda 表达式可以从周围的作用域中捕获变量,这使得它们非常适用于创建闭包和在 C++ 中使用函数式编程概念。
与传统函数相比,lambda 表达式具有一些性能优势:
- 内联函数:编译器会自动将 lambda 表达式内联,这意味着它们的代码直接插入到调用函数中。这可以减少函数调用的开销并提高性能。
- 避免命名函数的开销:lambda 表达式没有名称,因此它们不必被声明和存储在符号表中,这可以减少开销并提高性能。
- 改善高速缓存局部性:lambda 表达式可以在同一个函数中定义和使用,这意味着lambda使用的代码和数据将存储在与调用代码相同的高速缓存行中。这可以改善高速缓存局部性并降低高速缓存失效的成本。
- 减小代码大小:lambda 表达式通常比命名函数小,并且它们不需要外部函数调用,这可以减小编译代码的大小并提高性能。
- 增加灵活性:lambda 表达式可以用来将函数作为参数传递给其他函数,这提供了更大的灵活性,可以改善性能,减少重复代码的需求。
- 提高可读性:lambda 表达式可以通过以紧凑而自包含的方式封装复杂的逻辑来使代码更易于阅读。这可以通过使代码更易于理解和维护来提高性能。
总之,与传统函数相比,lambdas 可以通过减少开销、提高缓存位置、减少代码大小、提高灵活性和提高可读性来提高性能。