概念
- 本质 = 编译器自动生成的 匿名函数对象(仿函数)
- 可以定义在函数内部(普通函数不行)
- 没有可写出来的类型名 → 必须用 auto 接收
例子:
cpp
auto func = [](){ cout << "hello"; };
lambda表达式格式
cpp
[捕获列表] (参数) -> 返回值 { 函数体 }
-
[]捕获列表- 捕获外部变量给 lambda 用
- 不能为空 ,哪怕不捕获也要写
[]------必写 - 捕获方式:
[=]值捕获[&]引用捕获[x, &y]单独捕获
-
(参数)- 和普通函数参数一样
- 没有参数可以省略
()
-
-> 返回值- 返回值明确时 可以省略
- 无返回值也可以省略
-
{ 函数体 }- 和普通函数逻辑一样
- 不能为空 ,至少写
{}------必写
3. 最简单的 4 种写法(一看就会)
- 无参无返
cpp
[]{};
- 有参
cpp
[](int a, int b){ return a+b; };
- 有返回值(可省略->)
cpp
[](int a)->int{ return a*2; };
- 捕获外部变量
cpp
int x = 10;
auto f = [x](){ cout << x; };
捕捉列表
为什么需要捕捉?
Lambda 默认只能访问自身参数和函数体内变量 ,要使用外层作用域变量 ,必须在 [] 里声明捕捉。
三种捕捉方式
1. 显式捕捉(手动指定变量)
- 语法:
[变量名, &变量名, ...] - 含义:
[x, y]:x、y 为值捕捉(拷贝一份,lambda 内是副本)[&z]:z 为引用捕捉(直接用原变量,修改会影响外部)
- 例子:
[x, y, &z]→ x、y 值捕捉,z 引用捕捉
2. 隐式捕捉(自动识别变量)
- 语法:
[=]:隐式值捕捉 → 编译器自动捕捉 lambda 中用到的所有外层变量,全部按值传递[&]:隐式引用捕捉 → 编译器自动捕捉 lambda 中用到的所有外层变量,全部按引用传递
3. 混合捕捉(显式 + 隐式结合)
- 语法规则:
- 第一个元素必须是
=或& [=, &x]:其余变量隐式值捕捉,x 单独引用捕捉
例子:[&, x, y]:其余变量隐式引用捕捉,x、y 单独值捕捉
- 第一个元素必须是
- 注意:
- 以
=开头时,后面只能跟引用捕捉的变量 - 以
&开头时,后面只能跟值捕捉的变量
- 以
捕捉范围与特殊变量
- 局部域 lambda :只能捕捉 lambda 定义之前的局部变量
- 静态局部变量 / 全局变量 :不需要捕捉,可以直接在 lambda 里使用
- 全局 lambda :定义在全局作用域时,捕捉列表必须为空(没有可捕捉的局部变量)
关于const 与 mutable 修饰
- 默认:值捕捉的变量在 lambda 内是 const 的,不能修改
mutable修饰:- 放在参数列表后:
[x]() mutable { x++; } - 作用:取消值捕捉变量的 const 性,允许在 lambda 内修改
- 注意:修改的是副本 ,不会影响外部原变量;且参数列表
()不能省略(即使无参)
- 放在参数列表后:
lambda原理
本质:编译器生成的仿函数对象
- Lambda 本身是语法糖,编译后会被转换成一个匿名仿函数类 (重载了
operator()的类)。 - 从汇编/机器码层面看,不存在"lambda"这个概念,只有普通的类和函数调用。
- 这一点和范围 for 很像:范围 for 底层是迭代器,而 lambda 底层是仿函数。
仿函数类的结构映射
| Lambda 语法部分 | 对应仿函数类的结构 |
|---|---|
捕捉列表 [x, &y] |
类的成员变量 (值捕捉 → 成员变量拷贝;引用捕捉 → 成员变量是引用) |
参数列表 (int a) |
operator() 的参数列表 |
返回值 -> int |
operator() 的返回值类型 |
函数体 { ... } |
operator() 的函数实现 |
类名与唯一性
- 编译器会为每个 lambda 生成一个独一无二的类名(按内部规则生成,开发者无法直接写出)。
- 不同的 lambda(即使签名完全一样)也会生成不同的类,因此 lambda 没有可显式书写的类型,只能用
auto或模板参数接收。