如何将捕获不可拷贝对象的Lambda移动到std::function中?
问题原因
std::function<void()>的构造要求传入的可调用对象必须支持拷贝构造——它的内部类型擦除机制依赖拷贝语义管理存储的函数对象。你的Lambda捕获了不可拷贝的test_t对象,导致Lambda本身也成为不可拷贝类型,因此无法构造std::function。
解决方案
针对C++20环境,有两种可行的解决方式:
方式1:使用std::move_only_function(推荐)
C++20新增了std::move_only_function,这是专门为仅移动可调用对象设计的函数包装器,完全适配你的场景。修改代码如下:
#include <functional> class test_t { public: test_t() = default; ~test_t() = default; test_t(const test_t&) = delete; auto operator=(const test_t&) = delete; test_t(test_t&& other) noexcept = default; auto operator=(test_t&& other) noexcept -> test_t & = default; }; // 将参数替换为仅移动的函数包装器 auto func_a(std::move_only_function<void()> f) { } int main() { test_t a; auto f = [b = std::move(a)]() mutable {}; func_a(std::move(f)); // 编译通过 }
方式2:用智能指针间接持有test_t
如果编译器对std::move_only_function支持不全,可以通过std::shared_ptr包裹test_t,让Lambda的捕获变为可拷贝类型(因为std::shared_ptr支持拷贝),从而兼容std::function:
#include <functional> #include <memory> class test_t { public: test_t() = default; ~test_t() = default; test_t(const test_t&) = delete; auto operator=(const test_t&) = delete; test_t(test_t&& other) noexcept = default; auto operator=(test_t&& other) noexcept -> test_t & = default; }; auto func_a(std::function<void()> f) { } int main() { auto a_ptr = std::make_shared<test_t>(); auto f = [a_ptr]() mutable {}; // Lambda现在可拷贝 func_a(f); // 直接传递即可 }
额外选项:升级到C++23
C++23对std::function进行了扩展,允许构造仅移动的可调用对象。如果能升级编译标准,无需修改func_a的参数类型,直接使用std::move(f)传入原代码即可正常编译。
内容的提问来源于stack exchange,提问作者microlife
相关产品推荐
相关产品推荐

