C++14移动捕获仅可移动对象到std::function触发拷贝构造错误
问题根因
你遇到的编译错误和移动捕获逻辑无关,是std::function的标准约束导致的:C++23之前的std::function要求其包装的可调用对象必须满足*可拷贝构造(CopyConstructible)*的要求,哪怕你全程只用移动语义传递std::function对象,类型擦除的模板逻辑也会要求可调用对象的拷贝构造函数可用。
你通过移动捕获得到的lambda因为内部持有了只移的TResCarrier对象,自身的拷贝构造函数也被隐式删除,属于只移类型,自然不符合std::function的类型要求。编译报错就是因为编译器在实例化std::function的类型擦除逻辑时,尝试生成lambda的拷贝构造代码失败了。
可行解决方案
- 方案1:升级到C23,使用
std::move_only_function替换std::function
C23新增的std::move_only_function专门用于包装只移可调用对象,没有拷贝构造的要求,你的代码只需要修改一行声明即可,无额外运行时开销:std::move_only_function<void()> act = [temp = move(resCarrier), description]() mutable { try { auto c = make_unique<DataProcessor>(move(temp), description); c->doStuff(); } catch (...) {} }; scheduler->RunLater(move(act)); - 方案2:自定义只移可调用对象包装器
如果不能升级C++版本,且可以修改调度器的RunLater接口,可以自己实现一个仅支持移动语义的可调用对象包装器替换std::function,避免强制拷贝检查,同样没有额外开销。 - 方案3:用智能指针做一层兼容封装
如果不能修改调度器接口,只能使用std::function,有两种低成本兼容方式:- 用
std::shared_ptr包裹TResCarrier对象后再捕获,此时lambda会支持拷贝构造,符合std::function要求,引用计数开销在绝大多数业务场景下可忽略; - 不需要修改原资源类,直接将只移lambda用
std::shared_ptr包裹后再捕获,同样可以满足std::function的拷贝要求,代码示例:
auto lambda = [temp = move(resCarrier), description]() mutable { try { auto c = make_unique<DataProcessor>(move(temp), description); c->doStuff(); } catch (...) {} }; auto wrapped_lambda = std::make_shared<decltype(lambda)>(move(lambda)); std::function<void()> act = [wrapped_lambda]() { (*wrapped_lambda)(); }; scheduler->RunLater(move(act)); - 用
内容的提问来源于stack exchange,提问作者PasterOfMuppets
相关产品推荐
相关产品推荐

