Lambda 表达式(匿名函数)是一种可以在不定义函数名称的情况下直接定义并使用的小型函数。在 C++11 及更高版本中,Lambda 表达式使得编写临时函数变得更加简洁和直观。
Lambda 表达式通常用于:
Lambda 表达式的基本语法格式如下:
[捕获列表] (参数列表) -> 返回类型 { 函数体 }
捕获列表 (Capture List):
捕获列表用于指定外部变量如何在 Lambda 中使用。可以捕获外部作用域的变量,并决定是按值捕获还是按引用捕获。
[=]
:按值捕获外部作用域中的所有变量。
[&]
:按引用捕获外部作用域中的所有变量。
[var]
:按值捕获指定的变量 var
。
[&var]
:按引用捕获指定的变量 var
。
[=, &var]
:按值捕获所有外部变量,并按引用捕获指定变量 var
。
参数列表 (Parameter List):
Lambda 表达式接受的参数列表,和普通函数一样。
返回类型 (Return Type):
-> 返回类型
用于显式声明 Lambda 表达式的返回类型。C++11 之后可以省略返回类型,编译器会自动推断。
函数体 (Body):
函数体是 Lambda 表达式的执行逻辑,类似普通函数的实现部分。
在 C++ 中,捕获列表 是用于指定如何将外部变量传递到 Lambda 表达式中的一部分。捕获列表可以选择按值(=
)或按引用(&
)来捕获变量,也可以只捕获特定的变量。接下来,我们将逐一讲解您提到的几种捕获方式。
[=]
:按值捕获外部作用域中的所有变量[=]
语法表示按值捕获所有外部变量。Lambda 表达式会创建外部变量的副本,并在 Lambda 内部使用这些副本。
#include
int main() {
int x = 10;
int y = 20;
// 按值捕获外部变量
auto print_values = [=]() {
std::cout << "x: " << x << ", y: " << y << std::endl;
};
x = 15; // 修改外部 x
y = 25; // 修改外部 y
print_values(); // 输出:x: 10, y: 20(因为按值捕获,使用的是 x 和 y 的副本)
return 0;
}
x: 10, y: 20
解释:
print_values
调用时,x
和 y
已经被修改,Lambda 仍然使用它们的原始值(10 和 20),因为它们在 Lambda 捕获时是按值捕获的。[&]
:按引用捕获外部作用域中的所有变量[&]
语法表示按引用捕获所有外部变量。Lambda 表达式将直接使用外部变量的引用。因此,对 Lambda 内部捕获变量的修改会影响外部原始变量。
#include
int main() {
int x = 10;
int y = 20;
// 按引用捕获外部变量
auto modify_values = [&]() {
x = 30; // 修改外部 x
y = 40; // 修改外部 y
};
modify_values(); // 调用 Lambda,修改外部变量
std::cout << "x: " << x << ", y: " << y << std::endl; // 输出:x: 30, y: 40
return 0;
}
x: 30, y: 40
解释:
x
和 y
时是按引用的,意味着修改 Lambda 内部的变量会直接影响外部变量 x
和 y
。[var]
:按值捕获指定的变量 var
[var]
语法表示只按值捕获指定的变量 var
。仅捕获指定的变量并创建该变量的副本,其他变量则不会被捕获。
#include
int main() {
int x = 10;
int y = 20;
// 只按值捕获变量 x
auto print_value = [x]() {
std::cout << "x: " << x << std::endl;
};
x = 15; // 修改外部 x
print_value(); // 输出:x: 10(因为按值捕获,Lambda 使用的是 x 的副本)
return 0;
}
输出:
x: 10
解释:
x
是按值捕获的,所以即使在 Lambda 调用时修改了 x
,Lambda 使用的仍然是 x
的副本。[&var]
:按引用捕获指定的变量 var
[&var]
语法表示只按引用捕获指定的变量 var
,意味着 var
是通过引用捕获的,其他变量则不被捕获。
#include
int main() {
int x = 10;
int y = 20;
// 只按引用捕获变量 y
auto modify_y = [&y]() {
y = 30; // 修改 y
};
modify_y(); // 调用 Lambda,修改外部变量 y
std::cout << "y: " << y << std::endl; // 输出:y: 30
return 0;
}
y: 30
解释:
y
是按引用捕获的,因此 Lambda 内部对 y
的修改直接影响外部的 y
。但 x
没有被捕获,因此它没有受到影响。[=, &var]
:按值捕获所有外部变量,并按引用捕获指定变量 var
[=, &var]
语法表示按值捕获所有外部变量,并按引用捕获特定变量 var
。所有未显式列出的变量都会按值捕获,而 var
会按引用捕获。
#include
int main() {
int x = 10;
int y = 20;
// 按值捕获所有外部变量,按引用捕获 y
auto modify_values = [=, &y]() {
std::cout << "x: " << x << ", y: " << y << std::endl;
y = 50; // 修改 y
};
modify_values(); // 修改 y
std::cout << "x: " << x << ", y: " << y << std::endl; // 输出:x: 10, y: 50
return 0;
}
输出:
x: 10, y: 20
x: 10, y: 50
解释:
x
是按值捕获的,因此 Lambda 使用的是 x
的副本,外部 x
的值不会改变。
y
是按引用捕获的,因此在 Lambda 内部修改 y
时,外部的 y
也会被修改。
[=]
:按值捕获所有外部变量,变量的副本会传递给 Lambda,修改不会影响外部变量。
[&]
:按引用捕获所有外部变量,Lambda 内部的修改会直接影响外部变量。
[var]
:按值捕获指定变量,只有指定变量会按值传递给 Lambda。
[&var]
:按引用捕获指定变量,只有指定变量会按引用传递给 Lambda。
[=, &var]
:按值捕获所有外部变量,并按引用捕获指定变量。
#include
int main() {
// 定义一个 lambda 表达式,计算两个整数的和
auto add = [](int a, int b) -> int {
return a + b;
};
int result = add(5, 3); // 调用 Lambda 表达式
std::cout << "Result: " << result << std::endl; // 输出:Result: 8
return 0;
}
捕获列表:[]
,没有捕获外部变量。
参数列表:(int a, int b)
,表示 Lambda 表达式接收两个整数作为参数。
返回类型:-> int
,表示 Lambda 表达式返回一个整数。
函数体:return a + b;
,实现了加法操作。
#include
int main() {
int x = 10;
int y = 20;
// 按值捕获外部变量
auto add = [=]() -> int {
return x + y;
};
// 修改 x 和 y 的值
x = 15;
y = 25;
std::cout << "Result: " << add() << std::endl; // 输出:Result: 30
return 0;
}
捕获列表:[=]
,表示按值捕获所有外部变量。
返回值:即使 x
和 y
在 Lambda 表达式调用时被修改,Lambda 仍然使用它们被捕获时的值。
#include
int main() {
int x = 10;
int y = 20;
// 按引用捕获外部变量
auto add = [&]() -> int {
x += 5; // 修改外部变量 x
return x + y;
};
std::cout << "Result: " << add() << std::endl; // 输出:Result: 35
std::cout << "x: " << x << ", y: " << y << std::endl; // 输出:x: 15, y: 20
return 0;
}
[&]
,按引用捕获外部变量 x
和 y
,这意味着在 Lambda 表达式内对这些变量的修改会影响外部的变量。#include
int main() {
int x = 10;
int y = 20;
// 捕获特定变量(按值捕获 x 和按引用捕获 y)
auto add = [x, &y]() -> int {
y = y + 5; // 修改 y
return x + y; // 使用捕获的 x 和修改后的 y
};
std::cout << "Result: " << add() << std::endl; // 输出:Result: 35
std::cout << "x: " << x << ", y: " << y << std::endl; // 输出:x: 10, y: 25
return 0;
}
捕获列表:[x, &y]
,表示按值捕获 x
,按引用捕获 y
。x
的值不会变化,而 y
会发生变化。
调用结果:Lambda 使用捕获时的 x
(10),同时修改 y
的值。
#include
int main() {
// 一个不带参数的简单 lambda 表达式
auto greet = []() {
std::cout << "Hello, World!" << std::endl;
};
greet(); // 输出:Hello, World!
return 0;
}
捕获列表:[]
,没有捕获外部变量。
参数列表:()
,不接受任何参数。
函数体:输出 Hello, World!
。
Lambda 表达式常用于作为函数的参数,特别是在 STL 算法中。
#include
#include
#include
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用 lambda 表达式作为 std::for_each 的参数
std::for_each(numbers.begin(), numbers.end(), [](int num) {
std::cout << num << " ";
});
std::cout << std::endl;
return 0;
}
[](int num) { std::cout << num << " "; }
,在 std::for_each
中使用 Lambda 遍历容器 numbers
,并输出每个元素。C++14 引入了 Lambda 表达式返回类型推导,编译器会自动推导 Lambda 的返回类型,减少冗余的类型声明。
#include
int main() {
auto add = [](int a, int b) {
return a + b; // 自动推导返回类型
};
std::cout << "Result: " << add(5, 3) << std::endl; // 输出:Result: 8
return 0;
}
int
)。Lambda 表达式在 C++ 中提供了一种简洁的方式来创建匿名函数,它有以下特点:
简洁:无须为小功能编写额外的函数。
灵活:能够捕获外部变量,通过值或引用捕获外部作用域中的变量。
强大:广泛应用于 STL 算法中,作为函数参数传递。
临时函数:不需要重复使用的函数可以使用 Lambda 表达式。
回调函数:作为回调函数传递给 STL 算法或事件处理器。
事件处理:在 GUI 编程中,Lambda 表达式常用于信号和槽的连接。
Lambda 表达式极大地提高了代码的简洁性和可读性,是现代 C++ 编程的重要工具之一。