C++ Lambda 表达式

C++ Lambda 表达式允许我们定义匿名函数对象(functors),这些对象可以内联使用或作为参数传递。

Lambda 表达式是在 C++11 中引入的,用于以更方便、更简洁的方式创建匿名 functors。

它们之所以更方便,是因为我们不需要在单独的类或结构中重载 () 运算符。


创建 C++ Lambda 表达式

一个基本的 lambda 表达式可以看起来像这样

auto greet = []() {
  // lambda function body
};

这里,

  • [] 被称为 **lambda introducer(lambda 引入器)**,它表示 lambda 表达式的开始
  • () 被称为 **parameter list(参数列表)**,它类似于普通函数的 () 运算符

上面的代码等同于

void greet() {
  // function body
}

现在,就像普通函数一样,我们可以简单地使用以下方式调用 lambda 表达式

greet();

注意:我们使用了 auto 关键字来自动推导 lambda 表达式的返回类型。


示例:C++ Lambda 函数

#include <iostream>
using namespace std;

int main() {

// create a lambda function that prints "Hello World!" auto greet = []() { cout << "Hello World!"; };
// call lambda function greet(); return 0; }

输出

Hello World!

在上面的示例中,我们创建了一个简单的程序,该程序使用 C++ lambda 表达式打印 Hello World!

首先,我们创建了 lambda 函数并将其赋给一个名为 greet 的变量。

auto greet = []() {
  cout << "Hello World!";
};

然后,我们使用 greet 变量和 () 运算符调用了 lambda 函数

// displays "Hello World!"
greet(); 

带参数的 C++ Lambda 函数

就像普通函数一样,lambda 表达式也可以接受参数。例如,

#include <iostream>
using namespace std;

int main() {

// lambda function that takes two integer // parameters and displays their sum auto add = [] (int a, int b) { cout << "Sum = " << a + b; };
// call the lambda function add(100, 78); return 0; }

输出

Sum = 178

在上面的示例中,我们创建了一个 lambda 函数,该函数接受两个整数参数并显示它们的和。

auto add = [] (int a, int b) {
  cout << "Sum = " << a + b;
};

这相当于

void add(int a, int b) {
  cout << "Sum = " << a + b; 
}

然后,我们通过传递两个整数参数来调用 lambda 函数

// returns 178
add(100, 78);

带返回类型的 C++ Lambda 函数

与普通函数一样,C++ lambda 表达式也可以具有返回类型。

编译器可以根据 return 语句隐式推导出 lambda 表达式的返回类型。

auto add = [] (int a, int b) {
  // always returns an 'int'
  return a + b;
};

在上述情况下,我们没有显式定义 lambda 函数的返回类型。这是因为只有一个 return 语句,它总是返回一个整数值。

但是,对于返回不同类型的多个 return 语句,我们必须显式定义类型。例如,

auto operation = []  (int a, int b,  string op) -> double {
  if (op == "sum") {
    // returns integer value
    return a + b;
  } 
  else {
    // returns double value
    return (a + b) / 2.0;
  }
};

请注意上面代码中的 -> double。这显式地将返回类型定义为 double,因为根据 op 的值,有多个语句返回不同的类型。

因此,无论各种 return 语句返回哪种类型的值,它们都被显式转换为 double 类型。


示例 2:C++ Lambda - 显式返回类型

#include<iostream>
using namespace std;

int main() {

// lambda function with explicit return type 'double' // returns the sum or the average depending on operation auto operation = [] (int a, int b, string op) -> double { if (op == "sum") { return a + b; } else { return (a + b) / 2.0; } };
int num1 = 1; int num2 = 2; // find the sum of num1 and num2 auto sum = operation(num1, num2, "sum"); cout << "Sum = " << sum << endl; // find the average of num1 and num2 auto avg = operation(num1, num2, "avg"); cout << "Average = " << avg; return 0; }

输出

Sum = 3
Average = 1.5

在上面的示例中,我们创建了一个 lambda 函数来查找

  • 两个整数的和,或者
  • 两个整数的平均值
auto operation = []  (int a, int b,  string op) -> double {
  if (op == "sum") {
    // returns an 'int'
    return a + b;
  } 
  else {
    // returns a 'double'
    return (a + b) / 2.0;   
  }
};

main() 中,我们首先通过将 "sum" 作为第三个参数传递来查找 num1num2 的和

auto sum = operation(num1, num2, "sum");

在这里,即使 lambda 返回整数值,它也会被显式转换为 double 类型。

然后,我们通过传递另一个字符串作为参数来查找平均值

auto avg = operation(num1, num2, "avg");

C++ Lambda 函数捕获子句

默认情况下,lambda 函数无法访问封闭函数的变量。为了访问这些变量,我们使用捕获子句。

我们可以通过两种方式捕获变量

按值捕获

这类似于 按值调用函数。在这里,当 lambda 被创建时,实际值被复制。

注意:在这里,我们只能在 lambda 体内读取变量,但不能修改它。

具有按值捕获的基本 lambda 表达式如下所示

int num_main = 100;

// get access to num_main from the enclosing function
auto my_lambda = [num_main] () {
  cout << num_main;
};

在这里,[num_main] 允许 lambda 访问 num_main 变量。

如果我们从捕获子句中删除 num_main,我们将收到一个错误,因为 lambda 体无法访问 num_main

按引用捕获

这类似于 按引用调用函数,即 lambda 具有对变量地址的访问权限。

注意:在这里,我们可以在 lambda 体内读取变量并修改它。

具有按引用捕获的基本 lambda 表达式如下所示

int num_main = 100;

// access the address of num_main variable
auto my_lambda = [&num_main] () {
  num_main = 900;
};

请注意 [&num_main]& 运算符的使用。这表示我们正在捕获 num_main 变量的地址


示例 3:C++ Lambda 按值捕获

#include<iostream>
using namespace std;

int main() {

  int initial_sum = 100;

// capture initial_sum by value auto add_to_sum = [initial_sum] (int num) { // here inital_sum = 100 from local scope return initial_sum + num; };
int final_sum = add_to_sum(78); cout << "100 + 78 = " << final_sum; return 0; }

输出

100 + 78 = 178

在上面的示例中,我们创建了一个 lambda 表达式,该表达式返回局部变量 initial_sum 和整数参数 num 的和。

auto add_to_sum = [initial_sum] (int num) {
  return initial_sum + num;
};

这里,[initial_sum] 按值捕获了封闭函数中的 initial_sum

然后,我们调用函数并将返回存储在 final_sum 变量中。

int final_sum = add_to_sum(78);

在 lambda 函数内部

  • num78
  • initial_sum100

所以结果变成 100 + 78,即 178

注意:假设我们要按值捕获多个变量。例如,

auto my_lambda = [a, b, c, d, e] (){
  // lambda body
}

正如你所见,这可能是一项非常繁琐的任务。为了简化工作,我们可以使用**隐式按值捕获**。例如,

auto my_lambda = [=] (){
  // lambda body
}

在这里,[=] 表示封闭函数的所有变量都按值捕获。


示例 4:C++ Lambda 按引用捕获

#include <iostream>
using namespace std;

int main() {

  int num = 0;

  cout << "Initially, num = " << num << endl;
  
// [&num] captures num by reference auto increment_by_one = [&num] () { cout << "Incrementing num by 1.\n"; num++; };
// invoke lambda function increment_by_one(); cout << "Now, num = " << num << endl; return 0; }

输出

Initially, num = 0
Incrementing num by 1.
Now, num = 1

在上面的示例中,我们创建了一个 lambda 函数,该函数将局部变量 num 的值增加 1

auto increment_by_one = [&num] () {
  cout << "Incrementing num by 1.\n";
  num++;
};

这里使用 [&num] 按引用捕获 num

最初,num 的值为 0

然后,我们调用 lambda 表达式 increment_by_one()。这将 num 的值增加到 1

注意:要捕获封闭函数的所有变量,我们可以使用**隐式按引用捕获**。例如,

auto my_lambda = [&] (){
  // lambda body
}

这里,[&] 表示所有变量都按引用捕获。


示例:将 C++ Lambda 函数作为 STL 算法的参数

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std; int main() { // initialize vector of integers vector<int> nums = {1, 2, 3, 4, 5, 8, 10, 12};
int even_count = count_if(nums.begin(), nums.end(), [](int num) { return num % 2 == 0; });
cout << "There are " << even_count << " even numbers."; return 0; }

输出

There are 5 even numbers.

在上面的示例中,我们在 count_if 算法中使用了 lambda 函数来计算 nums 向量中偶数的总数。

int even_count = count_if(nums.begin(), nums.end(), [](int num) {
  return num % 2 == 0;
});

请注意,我们将 lambda 表达式作为第三个参数传递给了 count_if。lambda 表达式接受整数 num,如果 num 是偶数则返回 true

此外,我们是内联传递的 lambda 表达式。

[](int num) {
  return num % 2 == 0;
}

常见问题

我们可以在单个 lambda 捕获子句中同时使用按值捕获和按引用捕获吗?

是的,我们可以在单个 lambda 表达式中使用按变量捕获和按引用捕获。

例如,

  • [&num1, num2] - 按引用捕获 num1,按值捕获 num2
  • [&, num1, num2] - 按值捕获 num1num2,其余按引用捕获
  • [=, &num1, &num2] - 按引用捕获 num1num2,其余按值捕获
如何编写不使用 auto 关键字的 lambda 表达式?

我们可以使用 function<> 模板来实现这一点。

为此,我们首先需要包含 <functional> 头文件。

#include <iostream>
#include<functional>
using namespace std; int main() {
function<void(int, int)> add = [] (int a, int b) { cout << "Sum: " << a + b; };
add(1, 2); return 0; }

输出

Sum: 3

这里,function<void(int, int)> 明确表示 lambda 函数的类型为 void,并接受两个整数参数。

如何使用泛型 lambda?

在 C++14 中,引入了泛型 lambda 以支持 lambda 函数中的泛型模板参数。

例如,

#include <iostream>
using namespace std;

int main() {
auto display = [] (auto s) { cout << s << endl; };
display(1); display(2.5); display("Kathmandu"); return 0; }

输出

1
2.5
Kathmandu

在上面的示例中,我们使用单个 lambda 表达式来接受泛型类型参数并打印该值。

auto display = [] (auto s){
  cout << s << endl;
};

请注意参数列表中的 auto s 代码。这允许我们将不同类型的值传递给 lambda 表达式。

Lambda 中的 mutable 关键字是什么意思?

lambda mutable 关键字允许 lambda 修改**按值捕获**的变量,而不会影响其在封闭函数中的原始值。

但是,**按引用捕获**的变量不受影响。例如,

#include <iostream>
using namespace std;

int main() {

  int a = 1;
  int b = 1;

  cout << "In main():" << endl;
  cout << "a = " << a << ", ";
  cout << "b = " << b << endl;
  cout << endl;
  
auto add_one = [a, &b] () mutable { // modify both a & b a++; b++;
cout << "In add_one():" << endl; cout << "a = " << a << ", "; cout << "b = " << b << endl; }; add_one(); cout << endl; cout << "In main():" << endl; cout << "a = " << a << ", "; cout << "b = " << b << endl; return 0; }

输出

In main():
a = 1, b = 1

In add_one():
a = 2, b = 2

In main():
a = 1, b = 2

在上面的示例中,默认情况下 a 变量是不可修改的,因为它被**按值捕获**。

但是,mutable 关键字允许我们在不更改 main() 中原始值的情况下修改它。

auto add_one = [a, &b] () mutable {
  a++;
  b++;
  ...
};

这是因为 lambda 函数中的 a 变量是原始变量的一个独立副本。因此,更改它不会更改 main() 中的原始值。

如何创建立即调用的 lambda 表达式?

立即调用的 lambda 表达式是指在其定义后立即调用的 lambda 表达式。例如,

#include<iostream>
using namespace std;

int main(){

  int num1 = 1;
  int num2 = 2;

// invoked as soon as it is defined auto sum = [] (int a, int b) { return a + b; } (num1, num2);
cout << "The sum of " << num1 << " and " << num2 << " is " << sum; return 0; }

输出

The sum of 1 and 2 is 3

在上面的示例中,我们创建了一个 lambda 表达式来计算两个整数的和,该表达式立即使用参数 num1num2 调用。

auto sum = [] (int a, int b) {
  return a + b;
} (num1, num2);
C++ Lambda 表达式的扩展语法是什么?

C++ Lambda 表达式的扩展语法是

[capture_list] (parameter_list) mutable -> return_type {
  // lambda body
}

正如你所见,与我们在教程开头提供的基本语法相比,此语法更加全面。

这是因为它包含了我们到目前为止讨论的所有 lambda 元素

  • [capture_list] - 捕获封闭函数的变量
  • (parameter_list) - 要在 lambda 表达式内部使用的参数
  • mutable - 允许修改按值捕获的变量(在 lambda 内部)
  • return_type - 显式定义 lambda 表达式的返回类型
你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战