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>(); // 转换失败,无输出 }
核心逻辑
BaseState通过CRTP绑定派生状态类型,确保canTransition能检测到派生类的onExit特化。- 默认
onExit返回DefaultExitTag,特化版本返回void(或其他非标签类型)。 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>(); // 转换失败,无输出 }
核心逻辑
- 用
CanTransitionTo概念封装转换判断逻辑,代码语义更清晰。 - 状态类只需特化需要处理的
onExit方法,无需额外配置转换规则。
内容的提问来源于stack exchange,提问作者Sebastian Szczepański
相关产品推荐
相关产品推荐

