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" -- 几乎同时作用于两个参数。
作为我的问题的一个例子,假设一个基数 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" -- 几乎同时作用于两个参数。