为何被移动值的引用在目标左值超出作用域后不悬空?
关于Lambda引用捕获与右值传递的悬空引用疑问
研究Lambda引用相关用法时,我遇到一个本以为会因悬空引用崩溃的场景。我原本认为main函数里传给fn_x_3的Lambda是右值,会被移动到fn_x_3函数中并绑定到左值fn上。但程序最终输出了5,我疑惑的是:当fn_x_3的右值引用重载中的fn超出作用域时,左值引用重载里捕获的引用为何没有失效?
#include <iostream> #include <functional> std::function<int(int)> fn_x_3(std::function<int(int, int)>& fn) { // capture fn by reference! return [&fn](int x) { return fn(x, 3); }; } std::function<int(int)> fn_x_3(std::function<int(int, int)>&& fn) { // passing fn (lvalue) by reference to fn_x_3 overload return fn_x_3(fn); } int main() { // call to fn_x_3(function<int(int)>&& fn), lambda "moved into" the function auto fn = fn_x_3([](int x, int y){ return x + y; }); int result = fn(2); std::cout << result << std::endl; return 0; }
问题原因解析
- 首先要明确:当你把右值Lambda传给
fn_x_3的右值引用重载时,这个重载里的fn是右值引用绑定到临时对象后产生的左值,但这个临时对象的生命周期并没有在右值重载函数结束时立即结束。 - 在C++规则中,临时对象被绑定到函数参数的右值引用时,它的生命周期会延长到包含该函数调用的完整表达式结束。不过这里更关键的是:你在右值重载里调用了左值重载的
fn_x_3(fn),此时fn作为左值引用,指向的是那个临时的std::function对象。 - 但真正让测试结果“正常”的核心是运气(未定义行为):那个临时的
std::function对象(由Lambda隐式转换而来)在main函数的auto fn = fn_x_3(...)这个表达式结束后才会销毁。而你在表达式结束前就调用了fn(2),此时临时对象还处于生命周期内,所以引用有效。但如果把fn(2)的调用放到表达式结束之后,或者编译器优化策略不同,就极大概率会触发悬空引用,导致程序崩溃或出现不可预测的结果。 - 总结:你当前的测试结果只是巧合,这段代码本身存在严重的未定义行为,绝对不能依赖这个逻辑编写生产代码。
内容的提问来源于stack exchange,提问作者Niklas Vest
相关产品推荐
相关产品推荐

