Lamda表达式中捕获外部变量和传参有什么区别?
很好的问题!这是理解 Lambda 的关键。
本质区别
| 捕获变量 | 传参 | |
|---|---|---|
| 时机 | Lambda 定义时绑定 | Lambda 调用时传入 |
| 来源 | 外部作用域已有的变量 | 调用者主动提供的值 |
| 语法位置 | [] 里 |
() 里 |
代码对比
cpp
int offset = 10;
// ✅ 捕获:offset 在定义时就绑定了
auto withCapture = [offset](int x) { return x + offset; };
// ✅ 传参:offset 在调用时才传入
auto withParam = [](int x, int offset) { return x + offset; };
withCapture(5); // → 15,调用时不需要提供 offset
withParam(5, offset); // → 15,调用时必须提供 offset
捕获的两种方式:值 vs 引用
cpp
int count = 0;
// 值捕获 [count]:拷贝一份,外部变化不影响 Lambda 内部
auto byValue = [count]() {
// count++; // ❌ 编译错误!值捕获默认是 const
return count;
};
// 引用捕获 [&count]:共享同一变量,互相影响
auto byRef = [&count]() {
count++; // ✅ 可以修改
};
count = 99;
byValue(); // 返回 0(定义时拷贝的值)
byRef(); // count 变为 100
关键:值捕获在 定义那一刻 就拍了”快照”,之后外部怎么变都无关。
什么时候用捕获,什么时候用传参?
cpp
// 场景1:固定的"上下文"配置 → 用捕获
int threshold = 5;
auto filter = [threshold](int x) { return x > threshold; };
// threshold 是这个 Lambda 的"环境",每次调用都一样
std::vector<int> v = {1, 3, 7, 9, 2};
std::copy_if(v.begin(), v.end(), ..., filter); // 不需要每次传 threshold
// 场景2:每次调用都不同的数据 → 用传参
auto multiply = [](int x, int factor) { return x * factor; };
multiply(3, 2); // → 6
multiply(3, 10); // → 30
⚠️ 引用捕获的陷阱
cpp
// ❌ 危险:Lambda 生命周期超过了被捕获变量
auto makeLambda() {
int local = 42;
return [&local]() { return local; }; // local 已销毁!
}
auto fn = makeLambda();
fn(); // 💥 未定义行为,访问了悬空引用
// ✅ 安全:值捕获,拷贝一份带走
auto makeLambda() {
int local = 42;
return [local]() { return local; }; // 拷贝,安全!
}
规则:Lambda 如果要”逃出”当前作用域(存储、返回、异步),用值捕获。
一句话总结
传参 = 调用者每次决定给什么;捕获 = 定义时从周围环境”打包”带走,调用者无需关心。
想继续了解 mutable 关键字(让值捕获可修改)或者 Lambda 在 STL 算法中的实战用法吗?
