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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:54:54