C++:使用重载而不是动态转换通过基选择派生 Class

C++: Selecting a Derived Class through a Base Using Overloading instead of Dynamic Cast

作为我的问题的一个例子,假设一个基数 class 像这样:

struct Agent {

    void compete(const Agent& competitor) const = 0;

};

与这样的派生关联:

struct RockAgent;
struct PaperAgent;

struct ScissorsAgent: public Agent {

    void compete(const Agent& competitor) const override {
        if(dynamic_cast<const RockAgent*>(&competitor))
            std::cout << "I have lost" << std::endl;

        else if(dynamic_cast<const PaperAgent*>(&competitor))
            std::cout << "I have won!" << std::endl;

        //etc....
    }

};

并将其与此基数进行比较:

struct PaperAgent;
struct RockAgent;
struct ScissorsAgent;

struct Agent {

    void compete(const PaperAgent& competitor) const = 0;
    void compete(const RockAgent& competitor) const = 0;
    void compete(const ScissorsAgent& competitor) const = 0;

};

由此得出:

//forward needed classes.....

struct PaperAgent: public Agent {

    void compete(const PaperAgent& competitor) const override {
        std::cout << "I have won!" << std::endl;
    }

    //etc......

};

如果我尝试通过将代理多态实例(在本例中为引用)传递给 compete() 函数来使用这两种方法,则只有第一个会编译。在第二种情况下,编译器抱怨没有 compete(const Agent&) 这样的函数。我明白为什么这行不通,但是是否有其他替代方案不需要 dynamic_cast 并且在设计方面更接近上面显示的第二种情况?也许我不知道或者我从未想过的设计模式可以用来模拟这个?

更改Agent

struct Agent {
  virtual void competeWith(const Agent& competitor) const = 0;
  void compete(const Agent& competitor) const { compeditor.competeWith(*this); }
  virtual void compete(const PaperAgent& competitor) const = 0;
  virtual void compete(const RockAgent& competitor) const = 0;
  virtual void compete(const ScissorsAgent& competitor) const = 0;
};

在 PaperAgent 中:

struct PaperAgent: public Agent {
  void competeWith(const Agent& competitor) const override final {
    compeditor.compete(*this);
  }
  void compete(const PaperAgent& competitor) const final override;
  void compete(const RockAgent& competitor) const final override;
  void compete(const ScissorsAgent& competitor) const final override;  

};

这可能对 crtp 有帮助:

template<class D>
struct AgentImpl: public Agent 
  void competeWith(const Agent& competitor) const override final {
    compeditor.compete(*static_cast<D const*>(this));
  }
};
struct PaperAgent: public AgentImpl<PaperAgent>{
  void compete(const PaperAgent& competitor) const final override;
  void compete(const RockAgent& competitor) const final override;
  void compete(const ScissorsAgent& competitor) const final override;
};

减少代码复制。

a1.compete(Agent const& a2) 调用 a2.competeWith(a1),后者又使用 a2 的动态类型和完全重载解析调用 a1.compete(a2)

这是许多标准方法之一 "double dispatch" -- 几乎同时作用于两个参数。