【问题标题】:In C++11, dow can I reference an enum class defined inside a template argument在 C++11 中,我可以引用模板参数中定义的枚举类吗
【发布时间】:2016-02-09 19:17:52
【问题描述】:

这是我正在尝试编写的代码的简化版本:

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;  
};

我正在使用 c++11 和 clang。我在这里得到的错误是10:17: error: missing 'typename' prior to dependent type name 'Derived::State',用于声明SetState。我也尝试添加typename Derived::State DerivedState;,然后使用DerivedState 而不是Derived::State,但后来我得到error: unknown type name 'DerivedState'。

更令人困惑的是,我尝试了typedef typename Derived::State DerivedState;,然后我得到了错误:error: no type named 'State' in 'MyFSM'。我最后一次尝试是typedef enum class Derived::State DerivedState;,然后我得到了最令人困惑的错误:error: no enum named 'State' in 'MyFSM'。

我对这里的模板不够了解,欢迎任何帮助理解这一点!此外,这里可能有更好的模式可以使用。我的主要目标是StateMachine 类具有一些通用功能,然后有一堆不同的状态机,每个都有自己的状态和转换功能。我有另一种方法,需要将this 传递给SetState(当从MyFSM 内部调用时,这是我正在做的事情的简化版本),但这是丑陋的,因为它自己的原因。我很想避免这种带模板的类的事情,但我的主要目标是使派生类尽可能易于编写和理解。

【问题讨论】:

  • 你似乎混淆了typename和typedef。
  • @n.m,这不是问题。问题是如果没有Derived的完整定义,就找不到Derived::State。
  • @n.m.,没有Derived的完整定义,可以找到typename Derived::State。
  • @RSahu 我正在解决第一个编译错误。在这种情况下需要 typename 关键字。在解决第一个问题之前谈论其他问题是没有意义的。
  • @n.m.,OP 尝试了 typename 路线,但不知道如何从那里继续。

标签: c++ templates c++11 enums


【解决方案1】:

您可以通过将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);
}

【讨论】:

  • 非常好,是的,它可以推断出State 类型和is_same 调用验证你正在做一些理智的事情。谢谢!
【解决方案2】:

使用

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】:

    当编译器在class MyFSM : public StateMachine&lt;MyFSM&gt; 中到达public StateMachine&lt;MyFSM&gt; 时,它会尝试删除StateMachine&lt;MyFSM&gt;。当它这样做时,还没有看到MyFSM::State(Derived::State)。由于编译器不知道该类型,因此会发出错误。

    【讨论】:

      【解决方案4】:

      正如其他答案中提到的,问题是在实例化 CRTP Base 时,Derived is not full known。

      因此,一种选择是推迟确定您感兴趣的类型,如下所示:

      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);
        }
      };
      

      由于您可以专门化DeferredState 模板,因此编译器别无选择,只能等到您实际调用SetState 后再确定参数类型。那时,Derived 是完全定义的。你的问题就解决了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-18
        • 1970-01-01
        相关资源
        最近更新 更多