C++20移动捕获类实例报错:编译器调用已删除的拷贝构造函数
问题描述
想要为类B实现一个工厂函数,该函数需传入回调。工厂函数向B传入lambda,该lambda需要类A的实例,希望在工厂内创建A实例并移动捕获到lambda中。A不可拷贝但可移动,按预期应该可行,但编译器却尝试调用A已删除的拷贝构造函数,报错:use of deleted function 'A::A(const A&)'。
最小示例代码
#include <functional> class A { public: A() {} A(const A &) = delete; A& operator=(const A &) = delete; A(A&&) noexcept {} }; class B { public: explicit B(std::function<void()>) { }; }; B b_factory() { auto a { A() }; return B { [a = std::move(a)]() { // do something with a } }; } B b = b_factory();
编译器输出
$ arm-poky-linux-gnueabi-g++ --version arm-poky-linux-gnueabi-g++ (GCC) 9.3.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ arm-poky-linux-gnueabi-g++ -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a9 -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/home/.../poky_sdk_toolchain/sysroots/cortexa9hf-neon-poky-linux-gnueabi --sysroot=/home/.../poky_sdk_toolchain/sysroots/cortexa9hf-neon-poky-linux-gnueabi -I../include -O2 -pipe -g -feliminate-unused-debug-types -g -Wall -Wextra -Werror -Wno-psabi -std=gnu++2a -MD -MT src/lib/CMakeFiles/traplog.dir/move_example.cpp.o -MF src/lib/CMakeFiles/traplog.dir/move_example.cpp.o.d -o src/lib/CMakeFiles/traplog.dir/move_example.cpp.o -c ../src/lib/move_example.cpp In file included from /home/.../poky_sdk_toolchain/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/c++/9.3.0/functional:59, from ../src/lib/move_example.cpp:1: /home/.../poky_sdk_toolchain/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/c++/9.3.0/bits/std_function.h: In instantiation of 'static void std::_Function_base::_Base_manager<_Functor>::_M_clone(std::_Any_data&, const std::_Any_data&, std::false_type) [with _Functor = b_factory()::<lambda()>; std::false_type = std::integral_constant<bool, false>]': /home/.../poky_sdk_toolchain/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/c++/9.3.0/bits/std_function.h:211:16: required from 'static bool std::_Function_base::_Base_manager<_Functor>::_M_manager(std::_Any_data&, const std::_Any_data&, std::_Manager_operation) [with _Functor = b_factory()::<lambda()>]': /home/.../poky_sdk_toolchain/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/c++/9.3.0/bits/std_function.h:677:19: required from 'std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = b_factory()::<lambda()>; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = void; _ArgTypes = {}]': ../src/lib/move_example.cpp:27:5: required from here /home/.../poky_sdk_toolchain/sysroots/cortexa9hf-neon-poky-linux-gnueabi/usr/include/c++/9.3.0/bits/std_function.h:176:6: error: use of deleted function 'b_factory()::<lambda()>::<lambda>(const b_factory()::<lambda()>&)' 176 | new _Functor(*__source._M_access<const _Functor*>()); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../src/lib/move_example.cpp:24:26: note: 'b_factory()::<lambda()>::<lambda>(const b_factory()::<lambda()>&)' is implicitly deleted because the default definition would be ill-formed: 24 | [a = std::move(a)]() { | ^ ../src/lib/move_example.cpp:24:26: error: use of deleted function 'A::A(const A&)' ../src/lib/move_example.cpp:8:5: note: declared here 8 | A(const A &) = delete; | ^
问题原因与解决方案
原因分析
std::function的核心要求之一是:它存储的可调用对象必须支持拷贝构造。你的lambda通过移动捕获持有了一个不可拷贝的A实例,因此这个lambda的拷贝构造函数被编译器隐式删除(拷贝lambda需要拷贝其捕获的A,而A的拷贝构造已被删除)。当std::function<void()>的构造函数尝试拷贝这个lambda时,就会触发编译错误。
解决方案
方案1:使用智能指针持有A实例
将A的实例封装在std::shared_ptr中,让lambda拷贝捕获这个可拷贝的智能指针,从而让lambda本身支持拷贝构造:
#include <functional> #include <memory> class A { public: A() {} A(const A &) = delete; A& operator=(const A &) = delete; A(A&&) noexcept {} }; class B { public: explicit B(std::function<void()>) { }; }; B b_factory() { auto a = std::make_shared<A>(); return B { [a]() { // do something with *a } }; } B b = b_factory();
方案2:使用C++23的std::move_only_function
如果编译器支持C++23,可以使用std::move_only_function,它专门用于存储仅可移动的可调用对象,无需支持拷贝:
#include <functional> // C++23标准中包含move_only_function class A { public: A() {} A(const A &) = delete; A& operator=(const A &) = delete; A(A&&) noexcept {} }; class B { public: explicit B(std::move_only_function<void()>) { }; }; B b_factory() { auto a { A() }; return B { [a = std::move(a)]() { // do something with a } }; } B b = b_factory();
方案3:调整B的构造函数为模板类型
如果不需要std::function的类型擦除特性,可以让B的构造函数接收模板参数,直接存储传入的lambda,避免std::function的拷贝要求:
#include <functional> #include <utility> class A { public: A() {} A(const A &) = delete; A& operator=(const A &) = delete; A(A&&) noexcept {} }; class B { public: template<typename Func> explicit B(Func&& func) : func_(std::forward<Func>(func)) {} private: std::decay_t<Func> func_; }; B b_factory() { auto a { A() }; return B { [a = std::move(a)]() { // do something with a } }; } B b = b_factory();
内容的提问来源于stack exchange,提问作者arno
相关产品推荐
相关产品推荐

