C++模板状态机实现中不完整类型无效使用问题解决
模板状态机循环依赖编译错误解决
问题概述
作为模板编程新手,我实现了一个通用模板状态机,初始版本可正常运行。但当修改IdleState的process函数,添加状态转移代码后,出现了编译错误,推测是RunState未完全定义导致的问题,希望在不分离.cpp与.hpp文件的前提下解决该问题。
初始可运行代码
#include <iostream> #include <variant> #include <tuple> template<template<typename, typename> typename TM, typename ...Ss> class StateMachine { public: StateMachine() : states_(Ss(this)...) {} template<typename S> void setState() { current_state_ = &std::get<S>(states_); } template<typename S, typename E> void emitEvent(E event) { setState<typename TM<S, E>::type>(); } void process() { std::visit([](auto &arg) {arg->process();}, current_state_); } private: std::tuple<Ss...> states_; std::variant<Ss*...> current_state_ { &std::get<0>(states_) }; }; template<typename SM> class State { public: State(SM* sm) : state_machine_(sm) {} virtual void process() {}; protected: SM* state_machine_; }; namespace impl { /* Forward declarations */ class RunState; class IdleState; /* Events */ struct StopEvent {}; struct StartEvent {}; /* Transition map */ template<typename S, typename E> struct TransitionMap; template<> struct TransitionMap<IdleState, StopEvent> { using type = IdleState; }; template<> struct TransitionMap<IdleState, StartEvent> { using type = RunState; }; template<> struct TransitionMap<RunState, StopEvent> { using type = IdleState; }; template<> struct TransitionMap<RunState, StartEvent> { using type = RunState; }; class IdleState : State<StateMachine<TransitionMap, RunState, IdleState>> { public: /* Use parent constructor */ using State<StateMachine<TransitionMap, RunState, IdleState>>::State; void process() override { std::cout << "IDLE" << std::endl; } }; class RunState : State<StateMachine<TransitionMap, RunState, IdleState>> { public: /* Use parent constructor */ using State<StateMachine<TransitionMap, RunState, IdleState>>::State; void process() override { std::cout << "RUN" << std::endl; state_machine_->emitEvent<RunState>(StopEvent{}); } }; } // namespace impl int main() { StateMachine<impl::TransitionMap, impl::RunState, impl::IdleState> sm; /* We start in RunState and RunState emits event inside it's process() * function that will switch to the IdleState */ sm.process(); // stdout: RUN /* Idle does not emit any events so we will stay in that state */ sm.process(); // stdout: IDLE sm.process(); // stdout: IDLE return 0; }
修改后的问题代码(仅IdleState部分)
class IdleState : State<StateMachine<TransitionMap, RunState, IdleState>> { public: /* Use parent constructor */ using State<StateMachine<TransitionMap, RunState, IdleState>>::State; void process() override { std::cout << "IDLE" << std::endl; state_machine_->emitEvent<IdleState>(StartEvent{}); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * This is problematic line */ } };
编译错误信息
/usr/include/c++/9/tuple: In instantiation of ‘struct std::_Tuple_impl<0, impl::RunState, impl::IdleState>’: /usr/include/c++/9/tuple:893:11: required from ‘class std::tuple<impl::RunState, impl::IdleState>’ best.cpp:29:20: required from ‘class StateMachine<impl::TransitionMap, impl::RunState, impl::IdleState>’ best.cpp:92:17: required from here /usr/include/c++/9/tuple:185:12: error: invalid use of incomplete type ‘class impl::RunState’ 185 | struct _Tuple_impl<_Idx, _Head, _Tail...> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ best.cpp:49:7: note: forward declaration of ‘class impl::RunState’ 49 | class RunState; | ^~~~~~~~ In file included from best.cpp:3: /usr/include/c++/9/tuple:192:39: error: invalid use of incomplete type ‘class impl::RunState’ 192 | typedef _Head_base<_Idx, _Head> _Base; | ^~~~~ best.cpp:49:7: note: forward declaration of ‘class impl::RunState’ 49 | class RunState; | ^~~~~~~~ In file included from best.cpp:3: /usr/include/c++/9/tuple: In instantiation of ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(_UHead&&, _UTail&& ...) [with _UHead = impl::RunState; _UTail = {impl::IdleState}; <template-parameter-2-3> = void; long unsigned int _Idx = 0; _Head = impl::RunState; _Tail = {impl::IdleState}]’: /usr/include/c++/9/tuple:969:63: required from ‘constexpr std::tuple<_T1, _T2>::tuple(_U1&&, _U2&&) [with _U1 = impl::RunState; _U2 = impl::IdleState; typename std::enable_if<((std::_TC<true, _T1, _T2>::_MoveConstructibleTuple<_U1, _U2>() && std::_TC<true, _T1, _T2>::_ImplicitlyMoveConvertibleTuple<_U1, _U2>()) && (! std::is_same<typename std::remove_cv<typename std::remove_reference<_Tp>::type>::type, std::allocator_arg_t>::value)), bool>::type <anonymous> = true; _T1 = impl::RunState; _T2 = impl::IdleState]’ best.cpp:9:38: required from ‘StateMachine<TM, Ss>::StateMachine() [with TM = impl::TransitionMap; Ss = {impl::RunState, impl::IdleState}]’ best.cpp:114:69: required from here /usr/include/c++/9/tuple:218:38: error: type ‘std::_Head_base<0, impl::RunState, false>’ is not a direct base of ‘std::_Tuple_impl<0, impl::RunState, impl::IdleState>’ 218 | _Base(std::forward<_UHead>(__head)) { }
问题分析与需求
经分析,错误根源在于循环依赖导致的类型不完全定义:IdleState继承的State模板参数是StateMachine<TransitionMap, RunState, IdleState>,而RunState此时仅为前向声明,未完成定义;当修改IdleState::process后,编译器需要实例化emitEvent相关模板,触发了对RunState完整类型的需求,进而引发编译错误。
需求:在不分离.cpp与.hpp文件的前提下,解决该编译错误。
内容的提问来源于stack exchange,提问作者konin
相关产品推荐
相关产品推荐

