捕获列表写在[]内、参数列表前,决定lambda可访问的外部变量;空[]不捕获,[=]按值捕获所有自动变量,[&]按引用捕获所有自动变量,但推荐显式列出如[x,&y]或[=,&z],类中需[this]或[*this]访问成员,引用捕获有悬垂风险,初始化捕获支持C++14移动和重命名。

捕获列表写法和基本规则
捕获列表决定 lambda 函数体能访问哪些外部变量,写在 [] 里,位置在参数列表前。空括号 [] 表示不捕获任何变量;[=] 按值捕获所有自动变量(局部变量);[&] 按引用捕获所有自动变量。
但实际中几乎不用 [=] 或 [&] 全局捕获——容易隐式捕获不需要的变量,导致对象生命周期问题或意外修改。推荐显式列出要捕获的项:
-
[x, &y]:按值捕获x,按引用捕获y -
[=, &z]:默认按值捕获全部,但z特别按引用(覆盖默认) -
[&x, this]:按引用捕获x,同时显式捕获this(用于访问成员)
捕获 this 和成员变量的陷阱
在类成员函数里定义 lambda,默认不捕获 this,所以不能直接用 member_var 或 member_func()。必须显式写 [this] 或 [=](后者会隐式包含 this)。
但注意:[=] 捕获的是 this 的值(即指针),不是成员副本;而 [*this](C++17 起支持)才真正按值拷贝整个对象(包括所有成员)。常见错误是以为 [=] 能安全延长局部对象生命周期,其实它只延长了指针有效时间,若对象已析构,this->xxx 就是悬垂访问。
示例:
struct S {
int a = 42;
void f() {
auto bad = [=]() { return a; }; // OK,但依赖 this 存活
auto good = [*this]() { return a; }; // C++17,真正拷贝成员
auto safe = [a]() { return a; }; // 最稳妥:只捕获需要的成员
}
};
值捕获 vs 引用捕获的生命周期风险
按引用捕获([&x])最危险:如果 lambda 在 x 生命周期结束后被调用,就会访问悬垂引用。典型场景是返回 lambda、存入容器、或交给异步任务。
按值捕获([x])看似安全,但要注意:如果 x 是指针或智能指针,捕获的只是该指针的副本,指向的对象仍可能被提前释放。
- 安全做法:只对 POD 类型或明确短生命周期内不会失效的变量用
[&x] - 跨作用域使用 lambda 时,优先用
[x]或[=],并确认被拷贝的对象可安全复制 - 捕获
std::shared_ptr是常见模式:既避免拷贝大对象,又靠引用计数保活[ptr](...){ ... }
捕获列表里的初始化捕获(C++14 起)
当需要重命名、移动语义或构造新对象时,用初始化捕获:[y = std::move(x)] 或 [name = expr]。它绕过“只能捕获当前作用域变量”的限制,还能触发移动构造。
常见用途:
- 避免重复计算:
[hash = compute_hash(s)]{ return hash; } - 转移独占资源:
[fp = std::move(file_stream)]{ fp - 重命名冲突变量:
[old_x = x]{ /* 用 old_x,避免和参数 x 同名 */ }
注意:初始化捕获的表达式在 lambda 创建时求值,不是调用时;且类型推导遵循 auto 规则(非 decltype),所以 [x = std::move(some_string)] 得到的是 std::string,不是 std::string&&。
捕获列表看着简单,但每个符号背后都绑着对象生命周期、复制开销和线程安全三根弦——写完记得问自己一句:这个 lambda 可能在哪被调用?那时我捕获的东西还在不在?
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











