Lambda未使用的捕获是否生效?异步场景下shared_ptr会被提前销毁吗?
首先直接给你结论:仅仅在Lambda中按值捕获shared_ptr<Foo>实例,即使你在Lambda里完全不使用它,也足够阻止foo被提前销毁,不用担心编译器的优化会搞砸这件事。
为什么捕获就足够?
shared_ptr的核心机制是引用计数:每一次拷贝shared_ptr,都会把引用计数加1;每一次销毁shared_ptr(比如Lambda被销毁时),引用计数减1。当引用计数降到0时,才会销毁指向的Foo对象。
你的代码里,Lambda按值捕获了foo(也就是拷贝了这个shared_ptr),这就意味着:
- 只要这个Lambda对象还存在(也就是
async_thing还持有它,直到回调被调用),这个拷贝出来的shared_ptr就会让原Foo的引用计数保持在至少1的状态。 - 只有当回调执行完毕、Lambda被销毁后,这个拷贝的
shared_ptr才会被析构,引用计数才会减少。
编译器会不会优化掉未使用的捕获?
这是你最关心的点,但放心——编译器绝对不能优化掉这个未使用的shared_ptr捕获。
C++标准对编译器优化有严格限制:如果一个操作有"可见副作用",编译器就不能随意省略它。而shared_ptr的拷贝和析构操作是有明确可见副作用的:它们会修改引用计数,而引用计数的变化直接决定了Foo对象的生命周期。编译器无法确定省略这个捕获会不会导致程序行为改变(比如Foo提前被销毁,异步操作访问悬空引用),所以它不会做这种优化。
额外提醒
如果你是按引用捕获shared_ptr(比如[&foo]),那情况就完全不同了——这时候Lambda里只是持有原shared_ptr的引用,不会增加引用计数,原shared_ptr如果被销毁(比如当前作用域结束),Foo还是会被销毁,这才会导致悬空引用。但你的代码里是按值捕获,所以没问题。
举个更明确的例子,即使你的Lambda写成这样:
[foo]() { /* 什么都不做 */ }
这个foo的拷贝依然会存在,引用计数依然会被维护,Foo对象会安全存活到Lambda被销毁。
内容的提问来源于stack exchange,提问作者z0r

