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

C++状态机:如何基于onExit特化自动推导状态转换类型?

基于onExit特化自动推导有限状态机状态转换的方案

需求背景

现有有限状态机实现中,需手动在BaseState的模板参数里指定当前状态可转换的所有目标状态。希望通过onExit方法的特化自动推导允许的状态转换,无需手动枚举可转换状态,同时避免手动返回布尔值标识转换权限的方式。


方案一:CRTP + SFINAE 检测(兼容C++17)

通过**奇异递归模板模式(CRTP)**封装状态类型,结合SFINAE判断是否存在针对目标状态的onExit特化版本,自动推导转换权限。

代码实现

#include <variant>
#include <iostream>
#include <type_traits>
#include <utility>

// 默认退出标签,用于区分默认onExit和特化版本
struct DefaultExitTag {};

// CRTP基类,用于获取派生状态的实际类型
template <typename Derived>
struct BaseState {
    // 默认onExit返回标签类型
    template <typename T>
    DefaultExitTag onExit() { return {}; }

    // 检测是否可转换到T:判断onExit<T>()的返回类型是否不是默认标签
    template <typename T>
    static constexpr bool canTransition() {
        return !std::is_same_v<
            decltype(std::declval<Derived>().template onExit<T>()),
            DefaultExitTag
        >;
    }
};

template <typename... Ts>
struct StateMachine {
    std::variant<Ts...> active;

    template <typename T>
    bool activate() {
        // 检查当前状态是否允许转换到T
        const bool canTransition = std::visit([](auto&& arg) {
            using CurrentState = std::decay_t<decltype(arg)>;
            return CurrentState::template canTransition<T>();
        }, active);
        
        if (!canTransition) return false;

        // 执行退出逻辑并切换状态
        std::visit([](auto&& arg) { arg.template onExit<T>(); }, active);
        active.template emplace<T>();
        
        return true;
    }
};

// 状态定义
struct B;

struct A : BaseState<A> {
    // 仅特化需要处理的状态转换退出逻辑
    template <>
    void onExit<B>() { std::cout << "A -> B" << std::endl; }
};

struct B : BaseState<B> {
    // 无特化onExit,所有转换均不允许
};

int main() {
    StateMachine<A, B> sm;

    sm.activate<B>(); // 输出"A -> B",转换成功
    sm.activate<A>(); // 转换失败,无输出
}

核心逻辑

  1. BaseState通过CRTP绑定派生状态类型,确保canTransition能检测到派生类的onExit特化。
  2. 默认onExit返回DefaultExitTag,特化版本返回void(或其他非标签类型)。
  3. canTransition通过判断onExit<T>()的返回类型是否为默认标签,自动推导是否允许转换。

方案二:C++20 概念约束(更简洁)

利用C++20的**概念(Concepts)**特性,直接定义“可转换到目标状态”的约束,代码可读性更强。

代码实现

#include <variant>
#include <iostream>
#include <type_traits>
#include <concepts>

// 默认退出标签
struct DefaultExitTag {};

struct BaseState {
    template <typename T>
    DefaultExitTag onExit() { return {}; }
};

// 定义概念:判断状态State是否可转换到T
template <typename State, typename T>
concept CanTransitionTo = !std::is_same_v<
    decltype(std::declval<State>().template onExit<T>()),
    DefaultExitTag
>;

template <typename... Ts>
struct StateMachine {
    std::variant<Ts...> active;

    template <typename T>
    bool activate() {
        const bool canTransition = std::visit([](auto&& arg) {
            using CurrentState = std::decay_t<decltype(arg)>;
            return CanTransitionTo<CurrentState, T>;
        }, active);
        
        if (!canTransition) return false;

        std::visit([](auto&& arg) { arg.template onExit<T>(); }, active);
        active.template emplace<T>();
        
        return true;
    }
};

// 状态定义
struct B;

struct A : BaseState {
    template <>
    void onExit<B>() { std::cout << "A -> B" << std::endl; }
};

struct B : BaseState {};

int main() {
    StateMachine<A, B> sm;

    sm.activate<B>(); // 输出"A -> B",转换成功
    sm.activate<A>(); // 转换失败,无输出
}

核心逻辑

  1. 用CanTransitionTo概念封装转换判断逻辑,代码语义更清晰。
  2. 状态类只需特化需要处理的onExit方法,无需额外配置转换规则。

内容的提问来源于stack exchange,提问作者Sebastian Szczepański

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 15:37:06