如何用可变模板简化状态机重复类型列表?兼谈智能指针使用
问题1:用可变模板避免重复定义类型列表
你可以通过集中管理状态类型列表,再用模板元编程自动生成States和CurrentState的别名,只需要写一次40个状态的类型:
方案1:直接用可变模板别名
先定义通用的模板别名,再用所有状态类型实例化:
// 通用模板:接收任意状态类型,生成带shared_ptr的tuple/variant template<typename... StateTypes> using States = std::tuple<std::shared_ptr<StateTypes>...>; template<typename... StateTypes> using CurrentState = std::variant<std::shared_ptr<StateTypes>...>; // 只需要在这里写一次所有40个状态类型 using MyStates = States<State1, State2, State3, /* ... 直到State40 */>; using MyCurrentState = CurrentState<State1, State2, State3, /* ... 直到State40 */>;
方案2:基于tuple的类型提取(更优雅,避免重复写类型列表)
先把所有状态类型放到一个tuple别名里,再通过模板提取类型参数生成目标别名:
// 第一步:集中定义所有状态类型(只写一次) using AllStateTypes = std::tuple<State1, State2, State3, /* ... 直到State40 */>; // 第二步:模板元编程提取tuple的类型,生成带shared_ptr的版本 template<typename Tuple> struct MakeStates; template<typename... Ts> struct MakeStates<std::tuple<Ts...>> { using Type = std::tuple<std::shared_ptr<Ts>...>; }; template<typename Tuple> struct MakeCurrentState; template<typename... Ts> struct MakeCurrentState<std::tuple<Ts...>> { using Type = std::variant<std::shared_ptr<Ts>...>; }; // 最终的类型别名 using States = MakeStates<AllStateTypes>::Type; using CurrentState = MakeCurrentState<AllStateTypes>::Type;
这种方式只需要在AllStateTypes中维护一次状态类型列表,后续的States和CurrentState会自动同步更新。
问题2:结构体拥有状态时的智能指针选择
如果担心状态体积过大无法存放在栈上,使用智能指针是合理的选择,但要根据所有权场景选对类型:
- 如果结构体是这些状态的唯一所有者,优先用
std::unique_ptr:它是独占式智能指针,没有引用计数的额外开销,更高效。 - 如果需要在其他地方共享状态的所有权(比如多个状态机实例共用某些状态),再用
std::shared_ptr。
示例代码(用unique_ptr的情况):
// 基于前面的方案调整模板为unique_ptr template<typename... StateTypes> using States = std::tuple<std::unique_ptr<StateTypes>...>; struct StateMachine { States states; CurrentState current_state; // 构造时在堆上初始化所有状态 StateMachine() : states(std::make_unique<State1>(), std::make_unique<State2>(), /* ... 其他状态 */) {} };
此时结构体StateMachine拥有所有状态的所有权,状态对象存储在堆上,栈上仅存放指针组成的tuple,不会占用过多栈空间。
内容的提问来源于stack exchange,提问作者CrisPlusPlus
相关产品推荐
相关产品推荐

