You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

游戏引擎模块系统设计:解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 08:42:10