为何Lambda捕获列表中xx=std::move(x)会调用const拷贝构造函数?
问题分析与解答
代码示例
#include <iostream> #include <vector> #include <functional> using namespace std; struct B { B() { std::cout<<"B"<<std::endl; } B(B&& b2) { std::cout << "B&& from object moved" << b2.moved_ << std::endl; b2.moved_ = true; } B(const B&) { std::cout<<"const B&"<<std::endl; } // ~B(){ // std::cout<<"~B"<<std::endl; // } int bb=10; bool moved_=false; }; struct FF { FF(std::function<void()> ff):ff_(std::move(ff)){} std::function<void()> ff_; }; int main() { std::vector<int> a{1,2,3}; std::vector<FF> b{}; B bb; std::transform(a.cbegin(), a.cend(), std::back_inserter(b), [x = std::move(bb)](auto const i) { return FF([xx = std::move(x), i]() { std::cout<<"FF"<<i<<std::endl; }); }); for(auto const& j:b) { j.ff_(); } return 0; }
疑问
容器b是FF类型的vector,FF以std::function<void()>对象作为构造参数。当Lambda捕获列表中执行xx = std::move(x)时,为何会调用B的const B&拷贝构造函数?预期不应存在额外的构造函数调用。
原因与解决方法
核心原因
外层Lambda未添加mutable修饰符,导致其调用运算符operator()是const限定的:
- 默认情况下,Lambda的闭包类型是
const的,它的operator()成员函数会被隐式加上const修饰。 - 在这个
const成员函数内部,闭包的成员变量x会被视为const B类型。 - 执行
xx = std::move(x)时,std::move(x)会生成const B&&类型的右值,但你的B类仅定义了接受非const右值引用的移动构造函数B(B&&),无法匹配const B&&。 - 编译器找不到匹配的移动构造函数,只能退而调用接受
const B&的拷贝构造函数来初始化xx。
解决方法
给外层Lambda添加mutable修饰符,使其operator()不再是const限定:
std::transform(a.cbegin(), a.cend(), std::back_inserter(b), [x = std::move(bb)](auto const i) mutable { return FF([xx = std::move(x), i]() { std::cout<<"FF"<<i<<std::endl; }); });
此时闭包成员x是可修改的B对象,std::move(x)会生成B&&类型的右值,能够匹配B的移动构造函数,不会触发拷贝构造。
内容的提问来源于stack exchange,提问作者MengMeng
相关产品推荐
相关产品推荐

