导出继承sf::Drawable的类时无法在std::vector中使用的问题
解决动态库导出含自引用vector的SFML Entity类时的C2672编译错误
问题场景
使用SFML开发2D游戏引擎,将引擎从静态库转为动态库时,给继承sf::Transformable和sf::Drawable的Entity类添加__declspec(dllexport)后,因类内包含std::vector<Entity>成员,编译报错C2672: 'std::construct_at': 找不到匹配的重载函数,静态库模式下无此问题。
相关代码
Test.h
#pragma once #include <SFML/Graphics.hpp> #include <vector> #include <memory> class __declspec(dllexport) Entity : sf::Transformable, sf::Drawable { public: Entity() { }; ~Entity() { }; public: std::vector<Entity> children; };
Test.cpp
#include "Test.h"
错误信息
1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\include\xmemory(698,14): error C2672: 'std::construct_at': no matching overloaded function found 1>(compiling source file '/Window.cpp') 1> C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\include\xutility(388,16): 1> could be '_Ty *std::construct_at(_Ty *const ,_Types ...) noexcept(<expr>)' 1> C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\include\xmemory(698,14): 1> the template instantiation context (the oldest one first) is 1> C:\Users\jehud\source\repos\Harmony\Harmony\Test.h(19,22): 1> see reference to class template instantiation 'std::vector<Entity,std::allocator<Entity>>' being compiled 1> C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\include\vector(1476,26): 1> while compiling class template member function 'std::vector<Entity,std::allocator<Entity>> &std::vector<Entity,std::allocator<Entity>>::operator =(const std::vector<Entity,std::allocator<Entity>> &)' 1> C:\Users\jehud\source\repos\Harmony\Harmony\Test.h(20,1): 1> see the first reference to 'std::vector<Entity,std::allocator<Entity>>::operator =' in 'Entity::operator =' 1> C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\include\vector(1492,9): 1> see reference to function template instantiation 'void std::vector<Entity,std::allocator<Entity>>::_Assign_counted_range<Entity*>(_Iter,const unsigned __int64)' being compiled 1> with 1> [ 1> _Iter=Entity * 1> ] 1> C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\include\vector(1395,32): 1> see reference to function template instantiation 'Entity *std::_Uninitialized_copy_n<Entity*,std::allocator<Entity>>(_InIt,size_t,Entity *,_Alloc &)' being compiled 1> with 1> [ 1> _InIt=Entity *, 1> _Alloc=std::allocator<Entity> 1> ] 1> C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\include\xmemory(1865,18): 1> see reference to function template instantiation 'void std::_Uninitialized_backout_al<std::allocator<Entity>>::_Emplace_back<Entity&>(Entity &)' being compiled 1> C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\include\xmemory(1779,35): 1> see reference to function template instantiation 'void std::_Default_allocator_traits<_Alloc>::construct<_Ty,Entity&>(_Alloc &,_Objty *const ,Entity &)' being compiled 1> with 1> [ 1> _Alloc=std::allocator<Entity>, 1> _Ty=Entity, 1> _Objty=Entity 1> ]
核心原因
- 私有继承基类:C++中
class默认私有继承,导致sf::Transformable和sf::Drawable的构造/拷贝构造函数无法被标准库函数(如std::construct_at)访问,因为这些函数属于外部代码,无权访问私有基类的成员。 - 未实现抽象基类纯虚函数:
sf::Drawable是抽象基类,必须实现draw()纯虚函数,否则Entity属于抽象类,无法被实例化(而std::vector<Entity>需要创建Entity对象)。 - 动态库导出的默认拷贝操作冲突:添加
__declspec(dllexport)后,编译器生成的默认拷贝构造/赋值运算符的导出标记与基类的访问权限冲突,触发模板实例化时的构造函数匹配失败。
解决方案
1. 改为公有继承基类
将Entity对SFML基类的继承改为公有继承,确保外部代码能正确访问基类的构造逻辑:
class ENGINE_API Entity : public sf::Transformable, public sf::Drawable
2. 实现sf::Drawable的纯虚函数
必须实现draw()函数,使Entity成为可实例化的具体类:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override { states.transform *= getTransform(); for (const auto& child : children) { target.draw(child, states); } }
3. 使用导出/导入宏区分编译场景
定义宏避免动态库导出和导入时的重复标记问题,在动态库工程中添加ENGINE_EXPORTS预处理器定义:
#ifdef ENGINE_EXPORTS #define ENGINE_API __declspec(dllexport) #else #define ENGINE_API __declspec(dllimport) #endif
4. 显式定义拷贝构造和赋值运算符
显式实现这些操作,确保基类的拷贝逻辑被正确调用,同时保证函数能被动态库正确导出:
Entity(const Entity& other) : sf::Transformable(other), sf::Drawable(other), children(other.children) {} Entity& operator=(const Entity& other) { if (this != &other) { sf::Transformable::operator=(other); sf::Drawable::operator=(other); children = other.children; } return *this; }
修改后的完整代码
Test.h
#pragma once #include <SFML/Graphics.hpp> #include <vector> #ifdef ENGINE_EXPORTS #define ENGINE_API __declspec(dllexport) #else #define ENGINE_API __declspec(dllimport) #endif class ENGINE_API Entity : public sf::Transformable, public sf::Drawable { public: Entity() = default; ~Entity() = default; Entity(const Entity& other) : sf::Transformable(other), sf::Drawable(other), children(other.children) {} Entity& operator=(const Entity& other) { if (this != &other) { sf::Transformable::operator=(other); sf::Drawable::operator=(other); children = other.children; } return *this; } virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override { states.transform *= getTransform(); for (const auto& child : children) { target.draw(child, states); } } public: std::vector<Entity> children; };
Test.cpp
#include "Test.h"
内容的提问来源于stack exchange,提问作者J3hud
相关产品推荐
相关产品推荐

