为何添加std::move会使lambda的捕获对象回到栈上?
为何添加std::move会使lambda的捕获对象回到栈上?
先把你的代码贴出来方便大家理解:
#include <array> int test(void) { //auto big_lambda = new auto([d = std::array<int, 1024>{5,1,2}]() { return d[2]; }); auto big_lambda = new auto(std::move([d = std::array<int, 1024>{5,1,2}]() { return d[2]; })); auto res = (*big_lambda)(); delete big_lambda; return res; }
其实你遇到的问题核心是C++编译器对不同值类别表达式的初始化优化逻辑差异,咱们一步步捋清楚:
不加std::move的情况(注释行)
你写的[d = std::array<int, 1024>{5,1,2}]() { return d[2]; }是一个纯右值(prvalue)——简单说就是一个可以被编译器直接“原地构造”的临时表达式。
当你用new auto(prvalue)时,编译器可以直接在new分配的堆内存上构造这个lambda对象,连带着它的捕获成员d也直接在堆上初始化。整个过程中栈上不会出现这个1024个int的大数组,完美符合你“把捕获放堆上”的预期。
加了std::move的情况
当你给lambda套上std::move()后,这个表达式就变成了亡值(xvalue),编译器的处理逻辑直接变了:
- 首先会在栈上构造一个完整的临时lambda对象——包括它的捕获成员
d,也就是那个1024个int的数组会先在栈上占坑; - 然后再把这个栈上的临时lambda移动构造到堆上的内存里。
更关键的是,std::array是固定大小的聚合类型,它没有专门的移动构造函数——所谓的“移动”std::array本质就是逐元素拷贝。这就意味着栈上的临时数组不仅会被创建,还会完整拷贝到堆上,栈上的这个大数组痕迹根本没法被优化掉。
所以你看到的“捕获对象回到栈上”,其实是栈上多了一个临时的lambda捕获实例,这都是std::move改变了表达式值类别,打断了编译器的原地构造优化导致的。
备注:内容来源于stack exchange,提问作者Fullaccess
相关产品推荐
相关产品推荐

