搭建ECS架构时触发std::unique_ptr删除函数错误,求排查方案
问题背景
正在搭建基础的Entity Component System(ECS)架构,已定义如下类:
class Component { public: virtual ~Component() {}; // Ensure polymorphic behavior with a virtual destructor }; class TransformComponent : public Component { glm::vec2 position = {0.0f, 0.0f}; float rotation = 0.0f; float scale = 1.0f; }; using identifier = uuids::uuid; class Entity { private: identifier id; bool isActive = true; std::string name = "new entity"; std::vector<std::unique_ptr<Component>> components; public: Entity() : isActive(true), id( UUID::get()), name("NEW") {}; ~Entity() = default; // Disable copying Entity(const Entity&) = delete; Entity& operator=(const Entity&) = delete; // Define move constructor and move assignment Entity(Entity&& other) noexcept; Entity& operator=(Entity&& other) noexcept; template<typename T> T* GetOrAdd() { static_assert(std::is_base_of<Component, T>::value, "T must be derived from Component"); for (auto& comp : components) { if (auto result = dynamic_cast<T*>(comp.get())) { return result; } } auto newComp = std::make_unique<T>(); T* result = newComp.get(); components.push_back(std::move(newComp)); return result; } };
在Scene类中以std::unique_ptr<Entity>形式添加实体:
class Scene { public: Scene(std::string sceneName, bool isEditorMode); ~Scene(); private: std::unique_ptr<Entity> ent = std::make_unique<Entity>(); };
在HierarchyPanel构造函数中执行currentScene = *GlobalHelper::GetScenes()[0];时,出现如下编译错误:
D:\Projects\searchengineproject\vocabulary\game\src\rendering\HierarchyPanel.cpp: In constructor 'gl3::HierarchyPanel::HierarchyPanel()': D:\Projects\searchengineproject\vocabulary\game\src\rendering\HierarchyPanel.cpp:9:52: error: use of deleted function 'gl3::Scene& gl3::Scene::operator=(const gl3::Scene&)' 9 | currentScene = *GlobalHelper::GetScenes()[0]; | ^ In file included from D:\Projects\searchengineproject\vocabulary\game\src\rendering\HierarchyPanel.h:4, from D:\Projects\searchengineproject\vocabulary\game\src\rendering\HierarchyPanel.cpp:1: D:/Projects/searchengineproject/vocabulary/game/src/entities/Scene.h:21:11: note: 'gl3::Scene& gl3::Scene::operator=(const gl3::Scene&)' is implicitly deleted because the default definition would be ill-formed: 21 | class Scene { | ^~~~~ D:/Projects/searchengineproject/vocabulary/game/src/entities/Scene.h:21:11: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = gl3::Entity; _Dp = std::default_delete<gl3::Entity>]' In file included from D:/Program Files/CLion 2022.3.3/bin/mingw/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/bits/locale_conv.h:41, from D:/Program Files/CLion 2022.3.3/bin/mingw/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/locale:43, from D:/Program Files/CLion 2022.3.3/bin/mingw/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/bits/fs_path.h:37, from D:/Program Files/CLion 2022.3.3/bin/mingw/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/filesystem:45, from D:/Projects/searchengineproject/vocabulary/game/src/rendering/Texture.h:6, from D:/Projects/searchengineproject/VokabelTrainer/game/src/GlobalHelper.h:3, from D:\Projects\searchengineproject\vocabulary\game\src\rendering\HierarchyPanel.h:3, from D:\Projects\searchengineproject\vocabulary\game\src\rendering\HierarchyPanel.cpp:1: D:/Program Files/CLion 2022.3.3/bin/mingw/lib/gcc/x86_64-w64-mingw32/11.2.0/include/c++/bits/unique_ptr.h:469:19: note: declared here 469 | unique_ptr& operator=(const unique_ptr&) = delete; | ^~~~~~~~ mingw32-make[3]: *** [game\CMakeFiles\game_lab_3.dir\build.make:316: game/CMakeFiles/game_lab_3.dir/src/rendering/HierarchyPanel.cpp.obj] Error 1 mingw32-make[3]: *** Waiting for unfinished jobs.... mingw32-make[2]: *** [CMakeFiles\Makefile2:2095: game/CMakeFiles/game_lab_3.dir/all] Error 2 mingw32-make[1]: *** [CMakeFiles\Makefile2:2102: game/CMakeFiles/game_lab_3.dir/rule] Error 2 mingw32-make: *** [Makefile:1047: game_lab_3] Error 2
已查阅类似问题,解决方案均提及避免使用拷贝构造函数,且已在Entity类中禁用拷贝构造与赋值,声明了移动构造和赋值。请问还遗漏了什么?
错误原因
编译错误的核心是Scene类的拷贝赋值运算符被隐式删除:Scene类包含std::unique_ptr<Entity>成员,而std::unique_ptr本身是不可拷贝的(其拷贝赋值运算符被显式删除),因此编译器无法为Scene生成默认的拷贝构造和拷贝赋值函数。你仅处理了Entity类的移动语义,但Scene类的拷贝/移动语义未做处理,代码中currentScene = *GlobalHelper::GetScenes()[0];是在尝试拷贝赋值Scene对象,触发了被删除的拷贝赋值运算符。
解决方案
根据需求,有两种常见处理方式:
方式1:使用指针/引用代替对象拷贝
避免直接拷贝Scene对象,让currentScene持有Scene的指针或引用:
// 修改HierarchyPanel中currentScene的类型为指针 Scene* currentScene; // 构造函数中改为 currentScene = GlobalHelper::GetScenes()[0];
或使用引用(需确保引用的Scene对象生命周期始终有效):
// 修改HierarchyPanel中currentScene的类型为引用 Scene& currentScene; // 在构造函数初始化列表中初始化 HierarchyPanel::HierarchyPanel() : currentScene(*GlobalHelper::GetScenes()[0]) {}
方式2:为Scene类实现移动语义
若需要转移Scene的所有权,可为Scene类添加移动构造和移动赋值运算符,允许通过移动而非拷贝传递Scene:
class Scene { public: Scene(std::string sceneName, bool isEditorMode); ~Scene(); // 显式禁用拷贝 Scene(const Scene&) = delete; Scene& operator=(const Scene&) = delete; // 启用默认移动构造和赋值 Scene(Scene&& other) noexcept = default; Scene& operator=(Scene&& other) noexcept = default; private: std::unique_ptr<Entity> ent = std::make_unique<Entity>(); };
之后可通过移动操作赋值:
// 使用std::move转移所有权 currentScene = std::move(*GlobalHelper::GetScenes()[0]);
注意:移动后原Scene对象的ent成员会变为空,需确保后续代码不再使用原对象的无效成员。
内容的提问来源于stack exchange,提问作者notfynnaf

