Lambda函数的[=]和[&]

发布于:2025-07-07 ⋅ 阅读:(23) ⋅ 点赞:(0)

写之前先列一下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变量的自身地址或者参数,我对其成员变量进行修改,也是无法修改的,因为此时还是按值捕获,无法修改副本:

  • [=] 表示 按值捕获所有外部变量

  • 没有 mutableLambda 体内不能修改任何按值捕获的变量的副本

  • 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* 拷贝指针 ✅ 会影响 拷贝的是指针本身,修改的是它指向的内容