写之前先列一下Lambda函数常见用途:
1. 用于回调(如 Qt 信号槽)
connect(button, &QPushButton::clicked, this, [=]() {
qDebug() << "按钮被点击!";
});
2. 临时排序器、筛选器
std::vector<int> v = {5, 2, 3};
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b;
});
3. 简单替代函数对象或 std::function
std::function<int(int)> square = [](int x) { return x * x; };
qDebug() << square(5); // 输出 25
那么[=]和[&] 有什么区别,可以看下面列表:
Lambda 的捕获列表 [ ]
详解
捕获外部变量方式:
捕获方式 | 含义 |
---|---|
[ ] |
不捕获任何变量 |
[=] |
按值捕获外部所有变量(拷贝) |
[&] |
按引用捕获所有变量 |
[x] |
按值捕获 x |
[&x] |
按引用捕获 x |
[=, &x] |
其他按值捕获,x 按引用 |
[this] |
捕获当前类的 this 指针 |
[=, this] |
捕获成员变量 |
看似很简单,但是会有没有注意到的小细节,比如当使用[=]按值捕获变量的时候,我没有修改stat1变量的自身地址或者参数,我对其成员变量进行修改,也是无法修改的,因为此时还是按值捕获,无法修改副本:
[=]
表示 按值捕获所有外部变量没有
mutable
,Lambda 体内不能修改任何按值捕获的变量的副本stat1
是一个 结构体变量(非指针、非引用),被完整拷贝进 Lambda,不能被修改
#include <QDebug>
#include <memory>
typedef struct{
int aa;
QString bb;
}Group;
typedef struct{
int a;
Group b;
}Value;
int main(int argc, char *argv[])
{
Value stat1 ;
stat1.a=2;
auto f = [=]() {
stat1.a=3;//报错!!!!!!
qDebug() << stat1.a;//输出3
};
f();
qDebug() << stat1.a;//输出3
}
如果想不报错,可以:
1.加 mutable
(允许修改副本)
auto f = [=]() mutable {
stat1.a = 3; // OK:改的是副本 出了Lambda函数,修改无效
qDebug() << stat1.a;
};
2.用引用捕获(真的改外部变量)
auto f = [&]() {
stat1.a = 3; // OK:改的是外部 stat1 出了Lambda函数,修改生效
};
Lambda 中使用 [=]
捕值时,会拷贝变量副本,并默认视为 const,这个副本在 Lambda 内部是 const
的,不能修改它的任何部分,包括成员变量,除非加 mutable
。
但如果捕获的是指针或智能指针,由于它们本身指向堆区对象,即使是副本,也可以通过它们修改原始数据,无需 mutable
。
这和结构体是否浅拷贝或深拷贝无关,关键在于改的是副本本身,还是它所“指向”的内容。
✅ 举例总结:
捕获内容 | 捕获方式 | 修改内容 | 是否需要 mutable |
备注 |
---|---|---|---|---|
int x |
[=] |
❌(不能改) | ✅ 需要 | 捕获的是 const int 副本 |
Value v |
[=] |
❌(不能改) | ✅ 需要 | 捕获的是 const Value 副本 |
Value* v |
[=] |
✅(可改 *v) | ❌ 不需要 | 捕获的是指针,改的是指向内容 |
auto p = std::make_shared<Value>() |
[=] |
✅(可改 p->a) | ❌ 不需要 | shared_ptr 拷贝后共享资源 |
正确做法:
1.修改为指针
#include <QDebug>
#include <memory>
typedef struct{
int aa;
QString bb;
}Group;
typedef struct{
int a;
Group b;
}Value;
int main(int argc, char *argv[])
{
Value *stat1 =new (Value);
auto stat2 = std::make_shared<Value>();
stat1->a=2;
stat2->a=2;
stat1->b.aa=2;
stat2->b.aa=2;
auto f = [=]() {
stat1->a=3;
stat2->a=3;
stat1->b.aa=3;
stat2->b.aa=3;
qDebug() << stat1->a;//输出3
qDebug() << stat2->a;//输出3
qDebug() << stat1->b.aa;//输出3
qDebug() << stat2->b.aa;//输出3
};
f();
qDebug() << stat1->a;//输出3
qDebug() << stat2->a;//输出3
qDebug() << stat1->b.aa;//输出3
qDebug() << stat2->b.aa;//输出3
}
2. 修改为引用
#include <QDebug>
#include <memory>
typedef struct{
int aa;
QString bb;
}Group;
typedef struct{
int a;
Group b;
}Value;
int main(int argc, char *argv[])
{
Value stat1 ;
stat1.a=2;
auto f = [&]() {
stat1.a=3;
qDebug() << stat1.a;//输出3
};
f();
qDebug() << stat1.a;//输出3
}
✅ 补充总结:
捕获方式 | 是否允许修改 | 是否修改原始变量 | 是否需要 mutable |
---|---|---|---|
[=] |
❌ 默认不允许 | ❌ 改的是副本 | ✅ 需要 mutable 才能改副本 |
[=]() mutable |
✅ 可以修改副本 | ❌ | ✅ 用于修改副本 |
[&] |
✅ 允许修改 | ✅ 改的是原始变量 | ❌ 不需要 mutable |
可变 Lambda(mutable
)
默认按值捕获变量是只读的,如果你想修改,需要加 mutable,但是注意普通变量和指针变量的区别
:
#include <QDebug>
#include <memory>
typedef struct{
int a;
int b;
}Value;
int main(int argc, char *argv[])
{
int x = 5;
auto stat1 = std::make_shared<Value>();
int *y = new int(3); // y 指向一个值为 3 的 int
stat1->a=2;
auto f = [=]() mutable {
x += 1; // OK
stat1->a=3;
*y=5;
qDebug() << x;// 输出 6
qDebug() << stat1->a;//输出3
qDebug() << *y;//输出5
};
f();
qDebug() << x;//输出 5
qDebug() << stat1->a;//输出3
qDebug() << *y;//输出5
delete y;
}
这里要注意,*y和stat1->a的值,出来了Lambda函数变化生效,但是x的值出来了并没有生效,原因是
[=]
表示 按值捕获所有外部变量。所以
x
是拷贝进 Lambda的。虽然用了
mutable
,允许修改这个拷贝,但不会影响原来的x
。所以
qDebug() << x;
外部输出还是5
。
而
y是一个普通指针
,stat1
是一个 智能指针对象,即std::shared_ptr<Value>
。[=]
按值捕获的是这个指针对象的副本,但:副本仍然指向同一个堆内存;
所以修改
stat1->a
,即堆上的Value.a
,外部也会看到。
这和普通指针类似,拷贝指针后改指针所指的内容,外面也能看到。
变量名 | 捕获方式 | 类型 | 修改的是 | 是否影响外部 | 原因 |
---|---|---|---|---|---|
x |
[=] |
int |
拷贝 | ❌ 不会影响 | 普通值类型,lambda 中是副本 |
stat1 |
[=] |
shared_ptr |
拷贝指针 | ✅ 会影响 | 拷贝的是智能指针,多个拷贝共享一个对象 |
y |
[=] |
int* |
拷贝指针 | ✅ 会影响 | 拷贝的是指针本身,修改的是它指向的内容 |