为何捕获std::unique_ptr的lambda无法传入std::packaged_task?
问题分析与解决方案
核心原因
- 捕获
std::unique_ptr的lambda是不可拷贝的:由于std::unique_ptr禁用了拷贝构造,lambda的默认拷贝构造函数会被编译器自动删除,仅保留移动构造函数。 - C11/C14标准中,
std::packaged_task要求其存储的可调用对象必须满足CopyConstructible(可拷贝构造):无论你直接传入lambda还是用std::move传入,packaged_task内部都会尝试拷贝这个可调用对象,这就是两种写法都编译失败的根本原因。 - 从C17开始,标准放宽了
std::packaged_task的要求,仅需可调用对象支持移动构造即可,这也是升级到C17后第二种写法能正常工作的原因。
解决方案
方案1:升级到C++17及以上标准
如果你的编译器支持C++17,只需在编译时启用对应标准(例如GCC/Clang添加编译选项-std=c++17),你的第二种写法即可正常编译运行:
auto uptr1 = std::make_unique<int>(1); std::packaged_task<void()> task(std::move( [uptr = std::move(uptr1)]() { std::cout << *uptr << std::endl; }));
方案2:在C11/C14下包装不可拷贝的lambda
如果必须使用旧标准,可以将lambda包装在std::shared_ptr中,让packaged_task调用智能指针指向的lambda:
auto uptr1 = std::make_unique<int>(1); // 包装lambda到shared_ptr auto lambda_ptr = std::make_shared<decltype([uptr = std::move(uptr1)]() { std::cout << *uptr << std::endl; })>( [uptr = std::move(uptr1)]() { std::cout << *uptr << std::endl; }); // 构造packaged_task,通过指针调用lambda std::packaged_task<void()> task([lambda_ptr]() { (*lambda_ptr)(); });
或者使用std::bind绑定移动后的lambda(需注意lambda的生命周期):
auto uptr1 = std::make_unique<int>(1); auto lambda = [uptr = std::move(uptr1)]() { std::cout << *uptr << std::endl; }; std::packaged_task<void()> task(std::bind(std::move(lambda)));
内容的提问来源于stack exchange,提问作者cattt
相关产品推荐
相关产品推荐

