Lambda 表达式就是一个当场写、当场用、不用起名字的小函数。在 sort、for_each、STL 容器遍历里写回调非常方便,比单独写全局函数或仿函数更紧凑。
一、完整语法
1
| [捕获列表](参数列表) mutable -> 返回值类型 { 函数体 };
|
拆成 5 部分:
| 位置 |
名称 |
说明 |
[] |
捕获列表 |
把外面变量抓到 lambda 内部使用(最核心、最容易踩坑) |
() |
参数列表 |
和普通函数参数一样 |
mutable |
可选 |
允许修改「值捕获」的副本 |
-> 类型 |
尾置返回类型 |
可选,大部分情况编译器能自动推导 |
{} |
函数体 |
真正执行的代码 |
大括号后面的分号 ; 不要漏——lambda 整体是一个表达式,表达式必须以分号结尾。
二、捕获列表(最核心、最容易踩坑)
| 写法 |
含义 |
[] |
不捕获任何外部变量 |
[=] |
值捕获:复制一份进 lambda,只读;加 mutable 才能改副本,但不会改原值 |
[&] |
引用捕获:全部变量引用,lambda 内修改会直接改动原值 |
[x] |
只值捕获 x |
[&x] |
只引用捕获 x |
[=, &x] |
默认全部值捕获,唯独 x 用引用捕获 |
[this] |
类内使用,捕获当前对象的 this 指针 |
大坑:引用捕获不要捕获生命周期已经结束的变量(比如函数局部变量),如果 lambda 延迟执行、变量已经销毁,就会变成悬垂引用,访问直接未定义行为。
三、示例 1:基础 sort(竞赛最常用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> #include <vector> #include <algorithm> using namespace std;
int main() { vector<int> a = {3, 1, 4, 2}; sort(a.begin(), a.end(), [](int x, int y) { return x > y; }); for (auto num : a) cout << num << ' '; return 0; }
|
输出:
四、示例 2:捕获外部变量
1 2 3 4 5 6 7 8 9 10 11 12
| #include <iostream> using namespace std;
int main() { int limit = 10; auto f = [=](int x) { return x < limit; }; cout << f(5); return 0; }
|
五、示例 3:mutable 演示
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> using namespace std;
int main() { int a = 1; auto f = [=]() mutable { a++; cout << a << endl; }; f(); cout << a; return 0; }
|
输出:
六、示例 4:引用捕获
1 2 3 4 5 6 7 8 9 10 11 12
| #include <iostream> using namespace std;
int main() { int a = 1; auto f = [&]() { a++; }; f(); cout << a; return 0; }
|
七、存储 lambda:auto 与 std::function
lambda 的类型是匿名的独特类型,不能直接写类型名。一般两种存法:
- 直接
auto:最常用、最推荐。
std::function:需要把 lambda 当成「可调用对象」在函数之间传来传去时用。
1 2 3 4 5 6 7 8 9
| #include <iostream> #include <functional> using namespace std;
int main() { function<int(int)> f = [](int x) { return x * x; }; cout << f(3); return 0; }
|
八、易错点清单
[&] 大范围引用捕获要慎用。lambda 内部会"延长"被引用变量的生命周期错觉——其实是悬垂引用。一旦原变量被销毁,lambda 内访问直接未定义行为。
mutable 只对「值捕获」的副本生效。引用捕获本来就是原变量,加不加 mutable 都能改。
- 没写返回类型,编译器推导不出来时,lambda 会推导成
void。比如函数体只有一条 return x * x;(多分支)的情况,最好显式写 -> int。
- 捕获
this 后对象生命周期:lambda 内访问成员依赖原对象存活。如果 lambda 被存起来跨函数使用,原对象提前销毁就会出 bug。
九、极简速记
| 场景 |
推荐写法 |
| 只读访问外部变量 |
[=] |
| 修改外部变量 |
[&](慎用,注意生命周期) |
| sort 自定义排序 |
sort(..., [](a,b){ return a > b; }) |
| 函数间传递 |
std::function<R(Args...)> |
| 存储局部 lambda |
auto f = [...](){...}; |
十、小结
- Lambda 是匿名函数,当场写当场用,代码紧凑;
- 捕获列表是核心:
[=] 只读、[&] 可改原值;
- 优先用
auto,需要跨函数传递再考虑 std::function;
- C++11 引入,蓝桥杯 / 信奥 / 洛谷编译器全部支持。