Lambda(λ):编译器自动生成的匿名仿函数的语法糖。可以就地定义可调用对象,能够捕获上下文中的局部变量。
写 lambda,编译器后台自动生成一个独一无二的类(仿函数),[]里捕获的变量变成这个类的成员变量;()是函数形参;{}是函数体;operator()被重载。
1. 为什么需要Lambda
1.1 没有Lambda痛点
- 普通全局函数:无法直接访问局部变量,没有状态。
- 仿函数struct Functor{ operator() }:要单独写结构体,代码割裂,定义位置离调用点很远。
- std::bind:可以绑定参数,但语法晦涩,不支持直接捕获局部,复杂逻辑可读性差。
- 函数指针:不能保存状态,仅无捕获 lambda 可以转为函数指针。
1.2 Lambda解决的问题
- 就地定义匿名可调用对象,定义就在使用点附近,代码紧凑。
- 捕获周围局部变量,直接使用当前作用域变量,不用手动传参。
- 可以直接传给算法、std::function、std::thread、回调。
- 不需要起函数名,用完即弃,适合一次性回调逻辑。
注意:每一处 lambda,编译器都会生成一个全新的、唯一的匿名类型;不同 lambda 类型不相同,所以不能直接存容器,靠std::function做类型擦除统一保存。
2. 语法格式
[capture] (params) mutable noexcept -> return_type { body };
| 片段 | 名称 | 说明 |
|---|---|---|
| [capture] | 捕获子句,必须写 | 指定捕获哪些外部局部变量;[]不捕获任何变量 |
| (params) | 形参列表 | 和普通函数形参一样;C++14 支持 auto 泛型 lambda;无参数可以省略(除 mutable) |
| mutable | 可变性修饰 | 默认 lambda 的operator()是 const,不能修改按值捕获的成员;mutable 去掉 const,可以修改值捕获副本 |
| noexcept | 异常说明 | 可选,标记不会抛异常 |
| -> return_type | 尾置返回类型,可选 | 大部分情况编译器自动推导返回值;多分支返回类型不一致时必须显式指定 |
| 函数体 | 函数逻辑 |
2.1 捕获列表 [] 语法
| 捕获写法 | 含义 |
|---|---|
| [] | 不捕获任何变量,只能使用全局 / 静态变量 |
| [x] | 值捕获,拷贝 x 到 lambda 内部;lambda 内是副本,外部原始变量不受修改;默认 const,不能改副本,需要mutable |
| [&x] | 引用捕获,保存 x 的引用;直接操作外部原始变量;要警惕生命周期问题 |
| [=] | 值捕获全部用到的局部变量,自动拷贝,不捕获 this |
| [&] | 引用捕获全部用到的局部变量,全部拿引用 |
| [this] | 捕获当前类的 this 指针,可以访问类成员变量、成员函数 |
| [=, &a] | 混合捕获:默认全部值捕获,唯独 a 使用引用捕获 |
| [&, b] | 混合捕获:默认全部引用捕获,唯独 b 使用值捕获 |
- 值捕获示例:
int a = 10;
auto f = [a](){
// a = 20; //编译报错!值捕获默认const operator()
std::cout << a;
};
f();
- mutable:修改值捕获的副本
int a = 10;
auto f = [a]() mutable {
a = 99; //修改lambda内部副本,外部a不变
std::cout << a;
};
f();
std::cout << a; //仍然是10
- 引用捕获
int a = 10;
auto f = [&a](){
a = 99; //直接修改外部原始变量
};
f();
std::cout << a; //输出99
- this 捕获(类内部写 lambda)
struct Test{
int val = 100;
void run(){
auto f = [this](){
std::cout << val; //访问成员变量
};
f();
}
};
2.2 返回类型推导
- 函数体全部 return 返回同类型,编译器自动推导,可以省略-> type
- 如果不同分支返回不同类型,必须显式尾置返回
//自动推导返回int
auto add = [](int x,int y){ return x+y; };
//必须显式写返回类型
auto fun = [](bool flag) -> double {
if(flag) return 1.0;
else return 2;
};
- C++14 泛型 lambda,参数写 auto
auto glambda = [](auto a, auto b){ return a + b; };
glambda(1,2);
glambda(1.1, 2.2);
3. 应用场景
- STL 算法就地回调
std::sort、std::for_each、std::find_if,不用单独写函数 / 仿函数
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<int> v{3,1,4,2};
std::sort(v.begin(),v.end(), [](int a,int b){ return a>b; });
return 0;
}
- std::thread 线程执行体
#include <thread>
int main()
{
int x = 10;
std::thread t([&x](){
std::cout << x << std::endl;
});
t.join();
return 0;
}
- 存入std::function做回调、事件处理
#include <functional>
int main()
{
std::function<void(int)> callback;
int base = 100;
callback = [base](int v){ std::cout << v + base; };
callback(5);
return 0;
}
- 线程池 enqueue 传入任务
//直接传lambda给enqueue,内部会被std::bind包装成std::function<void()>
pool.enqueue([a,b](){
std::cout << a+b;
});
- std::bind 的替代品
//bind版本
auto f = std::bind(foo, std::placeholders::_2, std::placeholders::_1);
//等价lambda
auto f = [](auto x,auto y){ foo(y,x); };
浙公网安备 33010602011771号