[=]按值捕获变量,修改的是副本而非原变量;[&]易致悬垂引用;混合捕获仅支持[=, &x]或[&, x];mutable仅影响lambda内副本,不改变外部变量。
很多人以为 [=] 是“只读复制”,其实它只是按值捕获,不代表变量不可变。如果被捕获的是一个类对象,且该类的成员函数或重载操作符能修改自身状态(比如 std::vector::push_back()),那么 lambda 内部调用这些函数时,改的是副本——但你根本意识不到这个副本的存在,更不会想到它和原始变量毫无关系。
常见错误现象:[=] 捕获了一个 std::vector,lambda 里调用了 v.push_back(42),执行完后原 vector 一点没变,还纳闷“为什么没生效”。
[=] 对每个自动变量做拷贝构造(或移动,若满足条件),后续所有操作都作用于副本[&],或者显式列出引用捕获项如 [&v]
[=] 不会捕获 this 指针以外的任何隐式内容;在类成员函数中使用时,[=] 默认也按值捕获 *this(即复制整个对象),这通常不是你想要的[&] 看似方便,但它把所有自动变量都按引用捕获——而引用的有效性完全依赖于被引用变

典型场景:函数返回一个 lambda,里面用了 [&],调用方接收并 later 调用它:
auto make_bad_lambda() {
int x = 10;
return [&]() { return x; }; // ❌ x 是局部变量,函数返回后即销毁
}
// 后续调用会读取垃圾值,UB[&] 不检查变量是否“活得够久”,编译器几乎不报错(Clang/GCC 可能给 -Wreturn-stack-address 警告,但不覆盖所有情况)std::string_view)、或 std::optional::value() 返回的引用C++14 起支持混合捕获,但规则很严格:要么全部是值捕获,要么默认是值/引用捕获,再加显式例外。不能写成 [x, &y] 这种“部分显式值 + 部分显式引用”的形式(C++20 仍不合法)。
正确写法只有两种:
[=, &y]:默认按值,但 y 特别指定为引用[&, x]:默认按引用,但 x 特别指定为值错误写法示例:
int x = 1, y = 2;
auto bad = [x, &y]() { ... }; // ❌ 编译错误:C++ 不允许混用无默认的显式捕获另外注意:[=, &this] 是冗余的,因为 [=] 已隐含捕获 *this(按值),而 &this 意图捕获指针本身——但 this 是右值,不能绑定到非 const 左值引用,所以这条写法实际也通不过编译。
默认 lambda 是 const 的:即使按值捕获了 int x,你也无法在 lambda 体内给它赋值,除非加 mutable。但这只是解除了 lambda 自身副本的 const 限定,和外部变量完全无关。
容易误解的点:
[x]() mutable { x = 99; } → 改的是副本,外部 x 仍是原值[&x]() { x = 99; } → 直接改外部变量,无需 mutable[=]() mutable { /* 无法访问未捕获的变量 */ } → 仍然只能访问捕获列表中声明的那些真正需要 mutable 的典型场景是:内部缓存计算结果、实现伪随机数生成器的状态更新、或配合 std::function 存储可变状态的闭包——但务必清楚,这些状态全是 lambda 自己的私有副本。
最常被忽略的一点:捕获列表不是类型系统的一部分,同一个 lambda 表达式每次出现都会生成不同类型的闭包类。这意味着 auto 是最安全的推导方式;试图用 std::function 包装时,要注意值捕获带来的额外拷贝开销,尤其是捕获大对象(如 std::vector 或自定义结构体)时,[=] 可能悄悄触发深拷贝。