游戏引擎模块系统设计:解决typename提前引用的布局优化方案咨询
解决方案:Module类内部类型作为基类模板参数的问题
问题根源
C++类的继承列表属于类定义的前置部分,此时类的内部成员(如Stage枚举、TCreateValue类)还未被编译器解析,因此无法在继承列表的模板参数中直接引用这些未声明的内部类型。
方案一:用命名空间封装前置类型(最小侵入式)
把Stage和TCreateValue放到与Module同级的专属命名空间中,避免全局污染,同时让Module继承时能直接引用这些类型,之后在Module内部用using导入,保持对外接口的一致性。
代码示例:
namespace ModuleDetails { enum class Stage : uint8_t { Never, Always, Pre, Tick, Post, Render }; class TCreateValue; // 前置声明,依赖Module的前置声明 } // 前置声明Module,供TCreateValue使用 class Module; namespace ModuleDetails { class TCreateValue { public: std::function<std::unique_ptr<Module>()> create; Stage stage; }; } class Module : public ModuleFactory<Module, ModuleDetails::TCreateValue, ModuleDetails::Stage> { public: // 将外部类型导入Module内部,保持原有接口结构 using Stage = ModuleDetails::Stage; using TCreateValue = ModuleDetails::TCreateValue; }; // SomeClass使用方式完全不变 class SomeClass : public Module::Registry<SomeClass> { inline static bool Registered = Register(Module::Stage::Always); };
方案二:重构ModuleFactory,从Base类提取依赖类型(CRTP优化)
修改ModuleFactory的模板设计,仅传入Base类,通过CRTP(奇异递归模板模式)延迟类型解析,从Base类中提取所需的TCreateValue和注册参数类型,让Module可以先继承ModuleFactory,再在内部定义所需类型。
首先重构ModuleFactory:
template<typename Base> class ModuleFactory { public: // 从Base类中延迟提取所需类型 using TCreateValue = typename Base::TCreateValue; using Stage = typename Base::Stage; using TRegistryMap = std::unordered_map<TypeId, TCreateValue>; static TRegistryMap &ModuleRegistry() { static TRegistryMap registry; return registry; } template<typename T> class Registry : public Base { public: static T* Get() { return moduleInstance; } protected: static bool Register(Stage stage) { ModuleFactory::ModuleRegistry()[TypeInfo<Base>::template GetTypeId<T>()] = {[]() { moduleInstance = new T(); return std::unique_ptr<Base>(moduleInstance); }, stage}; return true; }; inline static T *moduleInstance = nullptr; }; };
然后修改Module类:
class Module : public ModuleFactory<Module> { public: enum class Stage : uint8_t { Never, Always, Pre, Tick, Post, Render }; class TCreateValue { public: std::function<std::unique_ptr<Module>()> create; Stage stage; }; };
SomeClass代码无需修改:
class SomeClass : public Module::Registry<SomeClass> { inline static bool Registered = Register(Stage::Always); };
该方案核心是利用模板实例化的延迟特性,ModuleFactory对Base内部类型的引用会等到Module完全定义后才解析,完美解决继承时的类型引用问题。
方案三:使用中间基类(分层定义)
如果不想改动原有ModuleFactory的模板参数结构,可以创建一个仅包含类型定义的中间基类,让Module先继承该类,再继承ModuleFactory:
// 中间基类,仅存放需要提前声明的类型 class ModuleBaseTypes { public: enum class Stage : uint8_t { Never, Always, Pre, Tick, Post, Render }; class TCreateValue { public: std::function<std::unique_ptr<class Module>()> create; // 前置声明Module Stage stage; }; }; class Module : public ModuleBaseTypes, public ModuleFactory<Module, ModuleBaseTypes::TCreateValue, ModuleBaseTypes::Stage> { // 直接使用继承自ModuleBaseTypes的类型 }; class SomeClass : public Module::Registry<SomeClass> { inline static bool Registered = Register(Stage::Always); };
该方案将类型定义与模块逻辑分层,适合需要保留原有ModuleFactory结构的场景。
内容的提问来源于stack exchange,提问作者xarxarx
相关产品推荐
相关产品推荐

