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

C++基于CRTP的无虚函数高效模块化系统设计问题求解

解决方案

核心思路

由于要求无虚函数、极致性能,我们采用编译期绑定+零开销调用的设计,完全规避运行期类型擦除的额外成本,同时解决参数冗余问题。

完整可运行实现

基础定义

#include <iostream>
#include <tuple>
#include <functional>

// 示例FunctionType枚举与Function类定义
enum FunctionType {
    FUNC1,
    FUNC2
};

template<FunctionType funcType>
struct Function {
    int doFunction(int param) const {
        return param * 2; // 示例实现
    }
};

// CRTP基类保持不变
template<class Derived>
class GenericModule {
private:
    Derived* crtp = static_cast<Derived*>(this);
public:
    void doSomething() { crtp->doSomething(); }
    void undoSomething() { crtp->undoSomething(); }
};

// 模块定义保持不变
template<FunctionType funcType, int param>
class ModuleA : public GenericModule<ModuleA<funcType, param>> {
private:
    Function<funcType> func;
public:
    void doSomething() { std::cout << param + 1 << "\n"; }
    void undoSomething() { std::cout << func.doFunction(param) << "\n"; }
};

template<FunctionType funcType, int param1, int param2>
class ModuleB : public GenericModule<ModuleB<funcType, param1, param2>> {
private:
    Function<funcType> func;
public:
    void doSomething() { std::cout << param1 + param2 << "\n"; }
    void undoSomething() { std::cout << func.doFunction(param1 + param2) << "\n"; }
};

方案1:编译期固定模块列表(性能最优)

完全编译期展开,开销等价于手动挨个调用模块方法,无任何额外成本:

template<FunctionType FuncType, typename... Modules>
class System {
private:
    std::tuple<Modules...> modules;

    template<typename F>
    void invoke_all(F&& call) {
        (std::invoke(call, std::get<Modules>(modules)), ...);
    }
public:
    explicit System(Modules... mods) : modules(std::move(mods)...) {}

    // 内嵌别名自动注入FuncType,解决参数冗余问题
    template<int param>
    using ModuleA = ::ModuleA<FuncType, param>;
    template<int p1, int p2>
    using ModuleB = ::ModuleB<FuncType, p1, p2>;

    // 快速创建模块的工厂方法
    template<int param>
    static auto make_moduleA() { return ModuleA<param>{}; }
    template<int p1, int p2>
    static auto make_moduleB() { return ModuleB<p1, p2>{}; }

    void doEverything() {
        invoke_all([](auto& mod) { mod.doSomething(); });
    }
    void undoEverything() {
        invoke_all([](auto& mod) { mod.undoSomething(); });
    }
};

// 使用示例
int main() {
    // 无需重复指定FUNC1,所有模块自动绑定系统的FunctionType
    auto system = System<FUNC1>(
        System<FUNC1>::make_moduleA<1>(),
        System<FUNC1>::make_moduleB<1, 2>()
    );

    system.doEverything(); // 输出 2、3
    system.undoEverything(); // 输出 2、6
    return 0;
}

方案2:运行期动态添加模块(灵活轻量)

如果需要运行期动态增减模块,可采用函数指针数组存储调用入口,无虚函数,开销仅为vector遍历成本:

template<FunctionType FuncType>
class DynamicSystem {
private:
    std::vector<void(*)()> do_ops;
    std::vector<void(*)()> undo_ops;
public:
    // 同样提供内嵌别名避免参数重复
    template<int param>
    using ModuleA = ::ModuleA<FuncType, param>;
    template<int p1, int p2>
    using ModuleB = ::ModuleB<FuncType, p1, p2>;

    template<typename Derived>
    void add(GenericModule<Derived>& mod) {
        do_ops.push_back([&mod]() { mod.doSomething(); });
        undo_ops.push_back([&mod]() { mod.undoSomething(); });
    }

    void doEverything() {
        for (auto& op : do_ops) op();
    }
    void undoEverything() {
        // 如需逆序undo可反向遍历
        for (auto& op : undo_ops) op();
    }
};

// 使用示例
int main() {
    DynamicSystem<FUNC1> system;
    DynamicSystem<FUNC1>::ModuleA<1> m1;
    DynamicSystem<FUNC1>::ModuleB<1,2> m2;
    system.add(m1);
    system.add(m2);

    system.doEverything();
    system.undoEverything();
    return 0;
}

问题解决说明

  1. 模块注册/存储问题:
    • 编译期方案用std::tuple做类型安全的异质容器,折叠表达式展开调用,编译器会直接优化为顺序调用模块方法,无任何运行期开销
    • 动态方案用无捕获lambda转函数指针存储调用入口,比虚函数表调用开销更低,每个模块仅占用两个指针的存储成本
  2. FunctionType参数冗余问题:
    • 两个方案都通过System类的内嵌模板别名自动注入FuncType参数,实例化模块时仅需传入模块自身的参数,编译器会强制所有模块的FunctionType与所属系统一致,避免参数不匹配错误

内容的提问来源于stack exchange,提问作者skudo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 13:45:11