In C ++ 11, a dow can reference an enum class defined inside a template argument

Here is a simplified version of the code I'm trying to write:

template<typename Derived>
class StateMachine
{
public:
  void SetState(Derived::State s) {
    static_cast<Derived*>(this)->TransitionTo(s);
  }
};

class MyFSM : public StateMachine<MyFSM>
{
public:
  enum class State {
    State1,
    State2,
    State3
  };
  void TransitionTo(State s) {
    _state = s;
  }
private:
  State _state = State::State1;  
};

I am using C ++ 11 with clang. Error I get here 10:17: error: missing 'typename' prior to dependent type name 'Derived::State'for the announcement SetState. I also tried adding typename Derived::State DerivedState;and then using DerivedStateinstead Derived::State, but then I get error: unknown type name 'DerivedState'.

Even more confusing, I tried it typedef typename Derived::State DerivedState;, and then received an error: error: no type named 'State' in 'MyFSM'. My last attempt was typedef enum class Derived::State DerivedState;, and then I get very tangled issue for all: error: no enum named 'State' in 'MyFSM'.

! , . , , , StateMachine , , . , this SetState ( MyFSM, , ), . , , - , .

+4
4

, SetState :

#include <type_traits>

template<typename Derived>
class StateMachine
{
public:
  template <typename State>
  void SetState(State s) {
    static_assert(std::is_same<State, typename Derived::State>::value, "Not a derived state");
    static_cast<Derived*>(this)->TransitionTo(s);
  }
};

class MyFSM : public StateMachine<MyFSM>
{
public:
  enum class State {
    State1,
    State2,
    State3
  };
  void TransitionTo(State s) {
    _state = s;
  }
private:
  State _state = State::State1;
};

int main() {
    MyFSM fsm;
    fsm.SetState(MyFSM::State::State1);
    // error: static assertion failed: Not a derived state
    // fsm.SetState(0);
}
+5

void SetState(typename Derived::State s) {

, Derived .

, , .

#include <iostream>

using namespace std;

template<typename Derived, typename DerivedState>
class StateMachine
{
   public:
      void SetState(typename DerivedState::State s) {
         static_cast<Derived*>(this)->TransitionTo(s);
      }
};

class MyFSMState
{
   public:
      enum class State {
         State1,
         State2,
         State3
      };
};

class MyFSM : public MyFSMState, public StateMachine<MyFSM, MyFSMState>
{
   public:
      void TransitionTo(State s) {
         _state = s;
      }
   private:
      State _state = State::State1;  
};

int main()
{
   MyFSM v;
   v.TransitionTo(MyFSM::State::State2);
}
+3

public StateMachine<MyFSM> class MyFSM : public StateMachine<MyFSM>, StateMachine<MyFSM>. , MyFSM::State (Derived::State) . , .

+2

, Derived , CRTP-.

So one option is to defer the definition of the type you are interested in:

template<typename Derived, typename = void>
struct DeferredState
{
  using type = typename Derived::State;
};

template <typename Derived>
class StateMachine
{
public:
  template<typename T = void>
  void SetState(typename DeferredState<Derived, T>::type s)
  {
    static_cast<Derived*>(this)->TransitionTo(s);
  }
};

Since you can specialize a template DeferredState, the compiler does not have an option, but until you actually name it SetStatebefore determining the type of the argument. At this time, Derived is fully defined. And your problem has disappeared.

+1
source

Source: https://habr.com/ru/post/1628019/


All Articles