
文章目录
前言
在学习lambda表达式之前,我们所使用的可调用函数对象只有函数指针和仿函数对象,他们的定义都相对麻烦,而使用lambda表达式来定义可调用对象,既方便又简单。
1.lambda表达式基础语法
cpp
[capture-list] (parameters) -> ReturnType { FuntionBody }
capture-list:捕捉列表(空列表不能省略)parameters:参数列表(无参时可以省略)ReturnType:返回值类型(可以省略,编译器自动推导)FuntionBody:捕捉列表(空函数体不能省略)
代码示例:
cpp
#include <iostream>
using namespace std;
int main()
{
// 一个简单的lambda表达式
auto add1 = [](int x, int y)->int {return x + y; };
cout << add1(1, 2) << endl;
// 1、捕捉为空也不能省略
// 2、参数为空可以省略
// 3、返回值可以省略,可以通过返回对象自动推导
// 4、函数体不能省略
auto func1 = []
{
cout << "hello bit" << endl;
return 0;
};
func1();
int a = 0, b = 1;
auto swap1 = [](int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
};
swap1(a, b);
cout << a << ":" << b << endl;
return 0;
}
2.捕捉列表
2.1.捕捉方式分类
- 显式捕捉:
- 值捕捉:
[x, y](拷贝变量,不可直接修改) - 引用捕捉:
[&x, &y](引用变量,可修改原变量)
- 隐式捕捉
- 隐式值捕捉:
[=](以拷贝方式捕捉所有外部变量) - 隐式引用捕捉:
[&](以引用方式捕捉所有外部变量)
- 混合捕捉
[=, &x]:变量x单独用引用捕捉,其余变量用值引用捕捉[&, y]:变量y单独用值引用捕捉,其余变量用引用捕捉
2.2.特殊捕捉规则与mutable修饰
- 不可捕捉 的变量:全局变量、静态局部变量(直接使用即可)
- mutable作用:取消值捕捉的const属性 ,允许修改拷贝(不影响原变量)
代码示例:
cpp
#include <iostream>
using namespace std;
int main()
{
int a = 10, b = 20;
// 混合捕捉 + mutable
auto func = [=, &b]() mutable {
a++; // 允许修改(拷贝)
b++; // 引用捕捉,修改原变量
cout << "内部a: " << a << ", 内部b: " << b << endl;
};
func();
cout << "外部a: " << a << ", 外部b: " << b << endl;
return 0;
}
测试结果:

2.3.捕捉方式对比
| 捕捉方式 | 语法示例 | 变量访问形式 | 是否可修改原变量 |
|---|---|---|---|
| 显式值捕捉 | [x, y] | 拷贝 | 否 |
| 显式引用捕捉 | [&x, &y] | 引用 | 是 |
| 隐式值捕捉 | [=] | 拷贝 | 否 |
| 隐式引用捕捉 | [&] | 引用 | 是 |
| 混合捕捉 | [=, &x] | 混合 | x是,其余否 |
| mutable值捕捉 | x mutable | 拷贝 | 否 |
3.典型应用场景
3.1.sort函数中的自定义比较逻辑
代码示例:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Goods {
string _name;
double _price;
int _id;
Goods(const char* name, double price,int id)
:_name(name)
,_price(price)
,_id(id){ }
};
int main()
{
vector<Goods> goods = { {"苹果",3.5, 1}, {"橙子",4.67, 2},{"葡萄",3.45, 3} };
//按价格升序排列
sort(goods.begin(), goods.end(), [](const Goods& a, const Goods& b) {return a._price < b._price; });
for (auto g : goods) cout << g._id << " " << g._name << " " << g._price << endl;
return 0;
}
测试结果:

3.2.简化可调用对象定义
- 替代函数指针:不需要再单独定义函数
- 临时逻辑封装:线程执行函数、回调函数等
代码示例:
cpp
#include <iostream>
#include <thread>
using namespace std;
int main()
{
int num = 0;
// 线程执行逻辑用Lambda封装
thread t1([&num]() {
for (int i = 0; i < 10000; ++i) {
num++;
}
});
thread t2([&num]() {
for (int i = 0; i < 10000; ++i) {
num++;
}
});
t1.join();
t2.join();
cout << "num: " << num << endl;
return 0;
}
4.底层原理
4.1.核心本质
- 底层逻辑:Lambda表达式编译后会生成一个匿名仿函数类
- 对应关系:
捕捉列表 --> 仿函数类的成员变量
参数列表 --> 仿函数operator()的参数
函数体 --> 仿函数operator()的函数体
- Lambda表达式与仿函数的转换:
cpp
// Lambda表达式
auto rateCalc = [rate](double money, int year) {
return money * rate * year;
};
// 编译器生成的等价仿函数
class Lambda_Generated {
private:
double _rate; // 捕捉的变量作为成员变量
public:
Lambda_Generated(double rate) : _rate(rate) {}
double operator()(double money, int year) const {
return money * _rate * year; // 函数体与Lambda一致
}
};
4.2.汇编层面验证
- Lambda对象的创建:调用生成类的构造函数(传捕捉变量)
- Lambda的调用:本质是调用生成类(仿函数)的operator()方法
汇编代码:
cpp
// 下面operator()中才能使用
eax, [rate]
ecx,[r1]
00D8295C lea eax,[rate]
00D8295F push eax
eax
00D82960 lea ecx,[r2]
ecx,[r2]
00D82963 call `main '::`2'::<lambda_1>::<lambda_1> (0D81F80h)
// 函数对象
Rate r1(rate);
00D82968 sub esp, 8
00 D8296B movsd xmm0,mmword ptr [rate]
00 D82970 movsd mmword ptr [esp],xmm0
00D82975 lea ecx,[r1]
00D82978 call Rate::Rate (0D81438h)
r1(10000, 2);
00D8297D push 2
00D8297F sub esp, 8
00 D82982 movsd xmm0,mmword ptr [__real@40c3880000000000 (0D89B50h)]
00 D8298A movsd mmword ptr [esp],xmm0
ecx,[r1]
00D8298F lea ecx,[r1]
00D82992 call Rate::operator() (0D81212h)
// 汇编层可以看到r2 lambda对象调用本质还是调用operator(),类型是lambda_1,这个类型名
// 的规则是编译器自己定制的,保证不同的lambda不冲突
r2(10000, 2);
00D82999 push 2
00D8299B sub esp, 8
00 D8299E movsd xmm0,mmword ptr [__real@40c3880000000000 (0D89B50h)]
00 D829A6 movsd mmword ptr [esp],xmm0
ecx,[r2]
00D829AB lea ecx,[r2]
00D829AE call `main '::`2'::<lambda_1>::operator() (0D824C0h)
5.注意事项
- 一定不能引用捕捉已销毁的局部变量
- mutable修饰 后的值捕捉的修改,不会影响原变量
- 全局 作用域的 Lambda 捕捉列表必须为空
捕捉列表中参数相当于仿函数类中成员变量,设想成员变量中如果声明了一个静态变量,那么这个静态成员显然不和其他成员存储在同一片内存空间上,它的初始化顺序也是不确定的。因此全局作用域中,初始化列表不能传值(全局/静态变量)
- 大对象最好用引用捕捉,值捕捉拷贝开销太大
总结
Lambda表达式作为C++11的核心特性,极大简化了臃肿的代码:无需定义独立函数或仿函数,可在局部直接封装临时逻辑,让代码更紧凑直观;灵活的捕捉机制能直接复用上下文变量,省去参数传递的冗余,大幅提升了开发效率。