【问题标题】:Can a function return a pointer to its own type?函数可以返回指向其自身类型的指针吗?
【发布时间】:2018-03-30 13:09:38
【问题描述】:

我在一个类中实现了一个非常小的有限状态机,我最初的方法是尝试这个:

class Widget {
public:
  void update(float time_interval){
    current_state = current_state_(time_interval);
  }
private:
  std::function<  ??  > off_(float);
  std::function<  ??  > standby_(float);
  std::function<  ??  > locked_(float);

  std::function<  ??  > current_state_; // initialised to off_
};

每个状态都是一个返回状态的函数。但是我不知道如何声明一个返回类型包括其返回类型的函数。有没有办法打破递归?

相反,我使用了 enum class 和丑陋的 switch 声明。

【问题讨论】:

标签: c++ c++11 c++14 c++17


【解决方案1】:

如果我理解正确,您想返回状态链中下一个函数的函数引用,并且所有状态步骤函数都具有相同的签名?

我看到这里的问题是返回类型是函数本身,所以声明函数的类型会调用递归类型定义。

所以你想编写RetType fn_(float) 形式的函数,但RetType 的类型(基本上)是RetType fn_(float)。所以现在我们有了(RetType (*)(float)) fn_(float) 或类似的东西,但无论我们多么努力,我们都无法摆脱RetType。

如果不向前声明某些内容,您将无法关闭该递归。我们可以转发声明类并使用它们,所以让我们为您的函数指针编写一个简单的类包装器,这可能是@Jarod 所暗示的。现在,std::function 是您的函数的类包装器,但它需要显式类型声明,而我们没有。

class Widget;
class StateFn;
class StateFn
{
  Widget * THIS;
  StateFn (Widget::*Fn)(float);
public:
  /// Or you could rely on POD construction
  StateFn(Widget * THIS, StateFn (Widget::*Fn)(float))
  : THIS(THIS), Fn(Fn)
  {}
  StateFn operator()(float f)
  {
    return THIS->*fn(f);
  }
};

所以现在递归定义被打破了,我们的状态函数可以返回 StateFn 对象,我们可以调用它们。

【讨论】:

    【解决方案2】:

    我找到了解决方案——用std::unordered_map 代替丑陋的switch

    class Widget {
    public:
      enum class State { Off, Standby, Locked };
    
      void update(float time_interval) {
        current_state_ = (this->*state_fn_.at(state_))(time_interval);
      }
    
    private:
      State off_(float) {return ::has_power() ? State::Standby : State::Off;}
      State standby_(float);
      State locked_(float);
    
      const std::unordered_map<State, State(Widget::*)(float)> state_fn_{
          {State::Off,     &Widget::off_},
          {State::Standby, &Widget::standby_},
          {State::Locked,  &Widget::locked_}};
    
      State current_state_{State::Off};
    };
    

    它只比我在问题中输入的理想代码差了 1+3n 行代码 - 我对此非常满意!

    【讨论】:

    • 很公平,但如果您要引入状态枚举,不妨采用传统的 case 语句。
    • 您还可以考虑为每个状态创建一个新的仅派生虚拟方法类。
    • 我假设你的状态枚举不只是 3 个状态,所以它也必须变成多行。
    猜你喜欢
    • 1970-01-01
    • 2018-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-17
    • 1970-01-01
    相关资源
    最近更新 更多