一、 什么是 Lambda 表达式?为什么要用它?
你可以把 Lambda 表达式理解成一个**“临时的、匿名的、随手写的”小函数**。
- 匿名:它没有名字,不像普通函数需要
void myFunction()这样命名。 - 临时/内联:它通常直接在需要它的地方定义,比如作为另一个函数的参数,代码更紧凑。
核心优点:
- 代码简洁:尤其是在调用STL算法(如排序
sort、查找find_if)时,可以直接将操作逻辑写在算法旁边,而不是另外定义一个完整的函数,代码可读性更高。 - 逻辑集中:将小段的功能代码直接内联,让代码逻辑更加集中,易于维护。
- 状态捕获:它可以方便地“捕获”上下文中的变量,在函数内部使用,这是普通函数难以做到的。
二、 Lambda 的核心语法(从简到繁)
Lambda 的完整语法看起来有点复杂,但我们把它拆开来看,其实非常符合直觉。
[捕获列表] (参数列表) mutable -> 返回值类型 { 函数体 }
我们从最简单的形式开始,一步步加上这些组件。
1. 最基础的形式:[]{}
这是最简单的 Lambda,一个什么都不做,也不接收参数的匿名函数。
// 定义并立即调用一个简单的 Lambda
[] {
std::cout << "Hello, Lambda!" << std::endl;
}(); // 末尾的 () 表示立即执行它
2. 带有参数:[] (参数列表) {}
和普通函数一样,它可以接收参数。
// 定义一个名为 'add' 的 Lambda,它接收两个整数并返回它们的和
auto add = [](int x, int y) {
return x + y;
};
// 调用它
int result = add(5, 3); // result 的值是 8
std::cout << "5 + 3 = " << result << std::endl;
auto add = ...;这里auto关键字会自动推导出 Lambda 的复杂类型,是定义 Lambda 的常用方法。
3. 指定返回值类型:[] () -> 返回值类型 {}
大多数情况下,编译器可以根据 return 语句自动推断出返回类型,所以 -> 返回值类型 这部分通常可以省略。
只有在少数复杂情况下(例如函数体中有多个返回不同类型的语句),才需要明确指定。
// 编译器可以自动推断返回值为 double,所以 -> double 可以省略
auto divide = [](double a, double b) {
return a / b;
};
// 必须显式指定的例子 (虽然不常见)
auto complex_logic = [](int x) -> double {
if (x > 10) {
return x; // 返回 int
}
return 20.5; // 返回 double
// 编译器不知道该推断成 int 还是 double,需要你来指定
};
4. 捕获外部变量:[捕获列表] () {} (这是考试的绝对重点)
这是 Lambda 最强大、也最重要的功能。Lambda 默认无法访问其定义范围之外的变量,必须通过捕获列表 [] 来指定它想访问哪些变量,以及如何访问。
捕获方式分为两种:值捕获和引用捕获。
| 捕获方式 | 语法 | 解释 | 特点与风险 |
|---|---|---|---|
| 不捕获 | [] | 不捕获任何外部变量。 | 安全,但功能受限。 |
| 值捕获 | [var] | 将 var 复制一份到 Lambda 内部。 | 安全:Lambda 内修改 var 不影响外部。默认只读:不能修改这份副本 (除非用mutable)。 |
| 引用捕获 | [&var] | 将 var 的引用(可以理解为别名)传给 Lambda。 | 高效/危险:可直接修改外部 var。节省大对象的拷贝开销。风险:注意悬垂引用!如果外部 var 销毁了,Lambda 还在,调用它就会出大问题。 |
| 隐式值捕获 | [=] | 以值捕获方式捕获所有在 Lambda 中用到的外部变量。 | 写法简单省事。但可读性稍差,不易看出捕获了哪些变量。同样默认只读。 |
| 隐式引用捕获 | [&] | 以引用捕获方式捕获所有在 Lambda 中用到的外部变量。 | 写法最省事。但风险最高,容易不经意间修改外部变量,且同样有悬垂引用风险。 |
| 混合捕获 | [=, &var] | var 按引用捕获,其它用到的变量按值捕获。 | 灵活,按需控制。 |
[&, var] | var 按值捕获,其它用到的变量按引用捕获。 | 灵活,按需控制。 | |
this 指针 | [this] | 在类的成员函数中,捕获当前对象的 this 指针。 | 可以在 Lambda 中调用类的成员函数和访问成员变量。 |
【机试代码示例】
int main() {
int a = 10;
int b = 20;
int c = 30;
// 1. 值捕获: 复制 a, b 到 Lambda 内部
// 默认是 const 的,你不能在内部修改 a 或 b
// auto func1 = [=] { a = 100; }; // 这行会报错!
auto func1 = [=] { return a + b + c; }; // c 是参数,a,b是捕获的
std::cout << "func1: " << func1(50) << std::endl; // 输出 10 + 20 + 50 = 80
// 2. 引用捕获: 可以修改外部变量
auto func2 = [&](int x) {
a = 100; // a 被修改
b += x; // b 被修改
};
func2(5);
std::cout << "a=" << a << ", b=" << b << std::endl; // 输出 a=100, b=25
// 3. 混合捕获: a 按值,b 按引用
int a = 10;
int b = 20;
auto func3 = [a, &b](int x) {
// a++; // 错误! a 是值捕获,是只读的
b += a + x; // 正确, b 是引用,可以修改;a 是值,可以读取
};
func3(5);
std::cout << "a=" << a << ", b=" << b << std::endl; // 输出 a=10, b=35 (b = 20 + 10 + 5)
return 0;
}
5. mutable 关键字
mutable 的作用是允许你修改按值捕获的变量。记住,这个修改只影响 Lambda 内部的副本,不会影响外部的原始变量。
int x = 10;
// 使用 mutable 来修改值捕获的变量 x 的副本
auto func = [x]() mutable {
x = 99; // 修改的是 Lambda 内部的 x 副本
std::cout << "Inside Lambda, x = " << x << std::endl;
};
func();
std::cout << "Outside Lambda, x = " << x << std::endl;
// --- 输出 ---
// Inside Lambda, x = 99
// Outside Lambda, x = 10
三、 Lambda 的实际应用(机试高频考点)
Lambda 最强大的地方在于和 STL 算法的无缝结合。
1. 配合 std::for_each 遍历容器
std::vector<int> v = {1, 2, 3, 4, 5};
int sum = 0;
// 使用 Lambda 遍历向量并计算总和
// [&sum] 捕获外部 sum 的引用,以便在 Lambda 内部修改它
std::for_each(v.begin(), v.end(), [&sum](int n) {
sum += n;
});
std::cout << "Sum: " << sum << std::endl; // 输出 Sum: 15
2. 配合 std::sort 自定义排序
struct Student {
std::string name;
int score;
};
std::vector<Student> students = {{"Li Ming", 92}, {"Zhang San", 85}, {"Wang Wu", 95}};
// 使用 Lambda 定义排序规则:按分数从高到低排序
std::sort(students.begin(), students.end(), [](const Student& a, const Student& b) {
return a.score > b.score;
});
// 打印排序结果
std::cout << "Sorted by score (desc):" << std::endl;
for (const auto& s : students) {
std::cout << s.name << ": " << s.score << std::endl;
}
3. 配合 std::find_if 自定义查找
std::vector<int> numbers = {15, 22, 38, 41, 53};
// 查找第一个大于 30 的数字
auto it = std::find_if(numbers.begin(), numbers.end(), [](int n) {
return n > 30;
});
if (it != numbers.end()) {
std::cout << "First number greater than 30 is: " << *it << std::endl; // 输出 38
}
四、 备考要点与记忆技巧
-
分清“捕获”与“传参”:
[x]是捕获,在 Lambda 定义时就从外部把x"拿"进来了。(int x)是参数,在 Lambda 调用时才从外部把值传进来。
-
机试第一要点:捕获列表
[]=是赋值,联想为复制一份,所以是值捕获。&是C++里的引用符号,联想为引用。- 最关键的区别:引用捕获
[&]可以修改外部变量,值捕获[=]默认不行。
-
机试第二要点:与 STL 结合
- 务必熟练
sort,find_if,for_each,count_if等常用算法如何配合 Lambda 使用。这是最常见的考法。
- 务必熟练
-
注意悬垂引用 (Dangling Reference)
- 这是一个笔试和面试中可能问到的高级陷阱。如果一个 Lambda 引用捕获了局部变量,而这个 Lambda 的生命周期比局部变量更长(比如被返回或存储在别处),那么当局部变量销毁后,调用这个 Lambda 就会导致程序崩溃。
- 简单原则:如果 Lambda 会在当前作用域之外被调用,对外部变量最好使用值捕获。

被折叠的 条评论
为什么被折叠?



