C++ Lambda值捕获与引用捕获原理探究:为何值捕获输出旧值?
Lambda值捕获与引用捕获的核心差异及工作原理
你遇到的这个问题,本质是没搞清楚C++ lambda中值捕获和引用捕获的时机与底层实现逻辑,另外你对lambda和std::function的理解也有偏差,我来一步步给你拆解:
先把你的代码贴出来方便对照:
int main() { int x = 1; auto valueLambda = [=]() { std::cout << x << std::endl; }; auto refLambda = [&]() { std::cout << x << std::endl; }; x = 13; valueLambda(); // 输出1 refLambda(); // 输出13 }
首先纠正一个误解:Lambda不是“无存储的std::function”
Lambda本质是编译器自动生成的匿名类的实例对象,它有自己的内存布局(会存储捕获的变量或引用);而std::function是一个通用的可调用对象包装器,它可以容纳lambda、函数指针、仿函数等多种可调用类型,两者是完全不同的东西——lambda是具体的可调用对象,std::function是装这些对象的“容器”。
两种捕获的底层逻辑
1. 值捕获([=]):捕获的是创建时的拷贝
当你用[=]声明lambda时,编译器会做这些事:
- 生成一个匿名类,里面包含一个和被捕获变量
x类型相同的成员变量(比如叫captured_x); - 在你创建lambda对象
valueLambda的时候(也就是auto valueLambda = [...]这一行),就把当前x的值(此时x是1)拷贝到这个成员变量captured_x里; - 当你后续调用
valueLambda()时,lambda内部访问的是自己存储的那个captured_x,而不是原来的局部变量x。所以哪怕你之后把x改成13,也和lambda里的拷贝没关系,输出自然是1。
2. 引用捕获([&]):捕获的是原变量的引用
用[&]声明lambda时,逻辑是这样的:
- 生成的匿名类里,成员变量是一个指向原变量
x的引用(或者底层用指针实现,逻辑等价); - 创建
refLambda时,这个引用就绑定到了原局部变量x上; - 当你调用
refLambda()时,它会通过引用直接访问原变量x的当前值——此时x已经被改成13了,所以输出13。
关键知识点:捕获发生在lambda创建时,而非调用时
这是很多人踩坑的点:捕获动作是在你定义lambda对象的那一刻完成的,不是在你调用lambda函数的时候。所以:
- 值捕获拿到的是“快照”:创建lambda时变量的状态;
- 引用捕获拿到的是“实时链接”:调用lambda时变量的当前状态(前提是原变量还在生命周期内)。
另外要注意:如果引用捕获的lambda生命周期超过了原变量的生命周期(比如把lambda传到异步任务里,原函数已经返回,局部变量销毁),就会出现悬空引用,调用lambda会触发未定义行为,这是非常危险的。
内容的提问来源于stack exchange,提问作者Bryan Fok
相关产品推荐
相关产品推荐

