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

基于模板的简易FSM构造优化及模板使用咨询

构造初始状态的简洁方案

针对你当前需要手动嵌套std::make_unique的问题,这里有几种更简洁的实现方式:

1. 带完美转发的模板构造函数

直接在FSM中实现模板构造函数,接受状态子类的构造参数,内部自动创建std::unique_ptr,无需用户手动处理:

#include <memory>
#include <utility>
#include <type_traits>

class State {
public:
    virtual ~State() = default;
    virtual void Enter() = 0;
    virtual void Exit() = 0;
};

class FSM {
public:
    // 模板构造函数:接受任意State子类的构造参数
    template <typename DerivedState, typename... Args>
    explicit FSM(Args&&... args) 
        : current_state_(std::make_unique<DerivedState>(std::forward<Args>(args)...)) {
        // 编译期检查:确保传入类型是State的子类
        static_assert(std::is_base_of_v<State, DerivedState>, 
                      "DerivedState must inherit from State");
    }

    // 状态切换示例
    void TransitionTo(std::unique_ptr<State> new_state) {
        if (current_state_) current_state_->Exit();
        current_state_ = std::move(new_state);
        current_state_->Enter();
    }

private:
    std::unique_ptr<State> current_state_;
};

调用时直接传入初始状态的构造参数即可:

class InitialState : public State {
public:
    InitialState(int param, const std::string& msg) { /* 构造逻辑 */ }
    void Enter() override { /* 进入动作 */ }
    void Exit() override { /* 退出动作 */ }
};

// 简洁调用
FSM fsm{42, "initial state"};

若担心构造函数重载冲突,可改用std::in_place_type_t显式指定状态类型:

template <typename DerivedState, typename... Args>
explicit FSM(std::in_place_type_t<DerivedState>, Args&&... args) 
    : current_state_(std::make_unique<DerivedState>(std::forward<Args>(args)...)) {
    static_assert(std::is_base_of_v<State, DerivedState>, 
                  "DerivedState must inherit from State");
}

// 调用方式
FSM fsm{std::in_place_type<InitialState>, 42, "initial state"};

2. 静态工厂方法

把构造逻辑封装成静态成员函数,代码可读性更高:

class FSM {
public:
    template <typename DerivedState, typename... Args>
    static FSM Create(Args&&... args) {
        static_assert(std::is_base_of_v<State, DerivedState>, 
                      "DerivedState must inherit from State");
        return FSM{std::make_unique<DerivedState>(std::forward<Args>(args)...)};
    }

    // 其他方法...

private:
    // 私有构造函数:仅允许工厂方法调用
    explicit FSM(std::unique_ptr<State> initial_state) 
        : current_state_(std::move(initial_state)) {}

    std::unique_ptr<State> current_state_;
};

调用方式:

auto fsm = FSM::Create<InitialState>(42, "initial state");
模板在FSM场景的评价与建议

优点

  • 编译期类型安全:通过模板和static_assert/C++20概念,编译阶段就能确保状态类型符合要求,避免运行时类型错误。
  • 零额外运行开销:模板实例化在编译期完成,不会引入虚函数之外的额外开销;纯模板实现的无多态FSM,甚至可以让编译器完全优化掉状态切换的冗余代码。
  • 接口灵活性高:可针对特定状态类型做模板特化,或通过模板参数定制FSM行为(比如是否记录状态切换日志)。

缺点

  • 编译时间增加:每个不同的状态组合都会生成独立的模板实例,复杂FSM可能导致编译耗时变长。
  • 错误信息复杂:模板错误通常冗长且难以定位,多层模板嵌套时排查成本更高。
  • 潜在代码膨胀:大量不同状态类型可能导致可执行文件体积增大,不过现代编译器的链接时优化(LTO)能有效缓解这个问题。

实用建议

  1. 用C++20概念约束状态类型:替代static_assert,让错误信息更清晰,同时明确FSM对状态的接口要求:
    template <typename T>
    concept StateConcept = requires(T t) {
        { t.Enter() } -> std::same_as<void>;
        { t.Exit() } -> std::same_as<void>;
    };
    
    // 在模板构造函数中添加约束
    template <StateConcept DerivedState, typename... Args>
    explicit FSM(Args&&... args) { /* ... */ }
    
  2. 分离通用逻辑与模板代码:把FSM核心逻辑(比如状态切换流程)放在非模板基类中,模板部分只负责状态实例化和类型检查,减少代码重复。
  3. 避免过度模板化:如果所有状态遵循统一接口、无需定制逻辑,用多态+std::unique_ptr的基础方案足够,模板仅用来简化构造即可。
  4. 补充清晰文档:模板接口的使用方式不如普通接口直观,需在注释中明确构造参数、模板参数含义及合法状态类型要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 00:55:05