☰
C++ Lambda 表达式详解:从基本语法到捕获列表
2026/9/26 11:12:30 网站建设 项目流程

在学习 C++ 和 Qt 的过程中,经常会看到这样的代码:

auto bitsum = [](int x) -> int { int res = 0; while (x) { res += x % 10; x /= 10; } return res; };

或者在 Qt 的connect()中看到:

connect(button, &QPushButton::clicked, this, [sender]() { sender->sendData(); });

刚开始看到[]、[sender]很容易疑惑。

其实这两段代码使用的都是同一个 C++ 特性:Lambda 表达式。

本文从 Lambda 的基本语法开始,重点介绍最容易混淆的捕获列表。


一、什么是 Lambda 表达式?

Lambda 可以简单理解为:

在代码中直接定义一个匿名函数。

例如普通函数:

int add(int a, int b) { return a + b; }

如果这个函数只使用一次,其实没有必要专门给它起一个函数名。

可以直接写成 Lambda:

auto add = [](int a, int b) -> int { return a + b; }; cout << add(3, 5);

输出:

8

这里的add是一个变量,它保存了这个 Lambda。

因此可以通过:

add(3, 5);

调用它。


二、Lambda 的基本语法

Lambda 的常见完整形式是:

[capture](parameters) -> return_type { // 函数体 };

例如:

auto add = [](int a, int b) -> int { return a + b; };

可以拆成:

[] (int a, int b) -> int │ │ │ 捕获列表 参数列表 返回值类型

最后:

{ return a + b; }

是 Lambda 的函数体。


三、[]是什么?

Lambda 最前面的[]叫做:

捕获列表(Capture List)

这是 Lambda 与普通函数一个非常重要的区别。

捕获列表主要解决一个问题:

Lambda 如果需要使用外部作用域中的局部变量,该怎么办?

例如:

void test() { int x = 10; auto func = []() { cout << x; }; }

这段代码会出错。

因为x是test()中的局部变量,而 Lambda 默认不能直接访问这个外部局部变量。

可以通过捕获列表解决:

void test() { int x = 10; auto func = [x]() { cout << x; }; }

这里:

[x]

表示:

把外面的x捕获到 Lambda 中。


四、[x]:按值捕获

最基本的捕获方式是:

[x]

表示按照值捕获x。

例如:

int x = 10; auto func = [x]() { cout << x; }; func();

输出:

10

可以把它简单理解成 Lambda 保存了一份x的副本。

因此:

int x = 10; auto func = [x]() { cout << x; }; x = 20; func();

输出仍然是:

10

因为 Lambda 中保存的是捕获时的x副本。


五、[&x]:按引用捕获

如果写成:

[&x]

则表示:

按引用捕获x。

例如:

int x = 10; auto func = [&x]() { cout << x; }; x = 20; func();

输出:

20

因为 Lambda 访问的是外部原来的x。

甚至可以直接修改它:

int x = 10; auto func = [&x]() { x = 100; }; func(); cout << x;

输出:

100

所以可以简单记忆:

[x] → 按值捕获 [&x] → 按引用捕获

六、[]:不捕获任何外部变量

例如:

auto func = []() { cout << "Hello"; };

这里:

[]

表示:

不捕获任何外部局部变量。

前面 LeetCode 中的数位和代码就是这种情况:

auto bitsum = [](int x) -> int { int res = 0; while (x) { res += x % 10; x /= 10; } return res; };

这里的x并不是外部变量。

它来自:

(int x)

也就是说:

x → Lambda 的参数

而不是:

x → 外部变量

所以根本不需要捕获。


七、[=]:默认按值捕获

如果外面有多个变量:

int a = 10; int b = 20; int c = 30;

可以写:

auto func = [=]() { cout << a << endl; cout << b << endl; cout << c << endl; };

[=]表示:

Lambda 中使用到的外部局部变量,默认按照值捕获。

这样就不需要写:

[a, b, c]

八、[&]:默认按引用捕获

同理:

auto func = [&]() { a++; b++; c++; };

表示:

Lambda 中使用到的外部局部变量,默认按照引用捕获。

因此:

[=] → 默认按值捕获 [&] → 默认按引用捕获

这是非常常用的两种写法。


九、捕获列表可以混合使用

捕获列表并不只能全部按值或者全部按引用。

例如:

int a = 10; int b = 20; auto func = [a, &b]() { cout << a << endl; b++; };

这里:

[a, &b]

表示:

a → 按值捕获 b → 按引用捕获

也可以使用默认捕获方式,再单独指定某个变量。

例如:

[=, &b]

表示:

默认按值捕获,但是b按引用捕获。

而:

[&, a]

表示:

默认按引用捕获,但是a按值捕获。


十、Lambda 中的“参数”和“捕获”不要混淆

这是学习 Lambda 时非常容易混淆的地方。

例如:

auto func = [x](int y) -> int { return x + y; };

这里有三个不同的部分:

[x] → 捕获列表 (int y) → 参数列表 -> int → 返回值类型

其中:

x

是外部变量,通过捕获列表进入 Lambda。

而:

y

是调用 Lambda 时传入的参数。

例如:

int x = 10; auto func = [x](int y) -> int { return x + y; }; cout << func(5);

结果:

15

所以:

捕获列表解决的是“Lambda 如何使用外部变量”,参数列表解决的是“调用 Lambda 时如何传入参数”。


十一、Lambda 在 Qtconnect()中的使用

Lambda 在 Qt 中非常常见,尤其是connect()。

例如:

connect(button, &QPushButton::clicked, this, []() { qDebug() << "按钮被点击"; });

这里第四个参数就是一个 Lambda:

[]() { qDebug() << "按钮被点击"; }

因为这个 Lambda 不需要使用外部局部变量,所以捕获列表为空:

[]

十二、Qt 中为什么会出现[sender]?

再看一个实际例子:

Sender *sender = new Sender; connect(button, &QPushButton::clicked, this, [sender]() { sender->sendData(); });

这里:

Sender *sender = new Sender;

定义了一个局部变量:

sender

而 Lambda 中需要使用这个变量:

sender->sendData();

因此需要在捕获列表中写:

[sender]

表示:

按值捕获外部的sender变量。

这里尤其要注意:

Sender *sender

中的sender是一个指针变量。

所以:

[sender]

捕获的是这个指针变量,而不是Sender类型。

可以简单理解为:

外部: sender ─────────→ Sender对象 │ │ 按值捕获 ↓ Lambda: sender ─────────→ 同一个Sender对象

指针本身复制了一份,但是两个指针仍然指向同一个对象。


十三、[sender]和[&sender]的区别

假设:

Sender *sender = new Sender;

那么:

[sender]

表示:

按值捕获指针变量。

而:

[&sender]

表示:

按引用捕获指针变量。

也就是说,两者的区别是:

[sender] ↓ 复制一份指针 [&sender] ↓ 引用原来的指针变量

在 Qt 的很多场景中,如果只是需要 Lambda 使用这个指针,常见写法就是:

[sender]

十四、为什么 Lambda 在 Qt 中特别常见?

因为 Qt 的connect()可以直接接收 Lambda。

例如:

connect(button, &QPushButton::clicked, this, [this]() { ui->label->setText("Hello"); });

这里:

[this]

也是一种捕获。

它表示:

捕获当前对象的this指针。

于是 Lambda 中可以使用:

ui->label

等当前对象的成员。

因此你在 Qt 项目中看到:

[this]()
[sender]()
[&data]()

本质上都是在使用 Lambda 的捕获列表。


十五、Lambda 常见写法总结

可以先记住下面这张表:

写法含义
[]不捕获外部变量
[x]按值捕获x
[&x]按引用捕获x
[=]外部变量默认按值捕获
[&]外部变量默认按引用捕获
[a, &b]a按值,b按引用
[=, &b]默认按值,但b按引用
[&, a]默认按引用,但a按值

十六、回到最开始的 LeetCode 代码

现在再看这段代码:

auto bitsum = [](int x) -> int { int res = 0; while (x) { res += x % 10; x /= 10; } return res; };

就很好理解了。

auto bitsum ↓ 用一个变量保存 Lambda [] ↓ 不捕获外部变量 (int x) ↓ Lambda 的参数 -> int ↓ 返回 int { ... } ↓ Lambda 函数体

而 Qt 中:

[sender]() { sender->sendData(); }

则是:

[sender] ↓ 按值捕获外部的 sender () ↓ 没有参数 { sender->sendData(); } ↓ 使用捕获进来的 sender

因此,理解 Lambda 最重要的就是先分清楚这三个部分:

[capture](parameters) -> return_type

其中:

捕获列表决定 Lambda 能不能使用外部变量,以及如何捕获这些变量;参数列表决定调用 Lambda 时接收什么参数;返回值类型决定 Lambda 返回什么类型。

掌握这几个基本概念后,再看 C++ STL、Qtconnect()中的大量 Lambda 写法,就不会再觉得[]、[this]、[sender]很神秘了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询