i007.cc

i007.cc

优先队列-降维打击

05.价值资料

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 算法中的实战用法吗?

发表回复