我正在使用访问者模式,我有以下编译代码:
class DerivedVisitee;
class Visitor
{
public:
void visit(DerivedVisitee &v);
};
class Visitee
{
public:
virtual void accept(Visitor &v) = 0;
};
class DerivedVisitee : public Visitee
{
public:
void accept(Visitor &v) { v.visit(*this); }
};
我想为visit
的所有后代提供默认的Visitee
方法。因此,我尝试执行以下操作:
class DerivedVisitee;
class Visitor
{
public:
void visit(DerivedVisitee &v);
};
class Visitee
{
public:
virtual void accept(Visitor &v) { v.visit(*this); } // added implementation here
};
class DerivedVisitee : public Visitee
{
// removed overridden method
};
但编译因'void Visitor::visit(DerivedVisitee &)' : cannot convert argument 1 from 'Visitee' to 'DerivedVisitee &'
(MSVC)而失败。你能解释一下为什么会发生这种情况,以及做我想做的事情的正确方法是什么?
编辑: Visitor::visit
只需要处理DerivedVisitee
个对象;换句话说,我打算对Visitor::visit
的不同后代使用多个重载的Visitee
方法和不同的实现。
答案 0 :(得分:1)
基本答案是:你不能在纯面向对象的代码中。
本质上,访问者模式是关于传递给visit
派生类型,而Visitee
表示类型是未知的(它是运行时属性)。
在C ++中,存在一种名为CRTP的模式:
template <typename Derived, typename Base>
class VisiteeHelper: public Base {
public:
virtual void accept(Visitor& v) override {
Derived& d = static_cast<Derived&>(*this);
v.visit(d);
}; // class VisiteeHelper
然后你可以从中得出:
// And there we see the "Curiously Recurring" part:
class DerivedVisitee: public VisiteeHelper<DerivedVisitee, Visitee> {
}; // class DerivedVisitee
class MoreDerivedVisitee: public VisiteeHelper<MoreDerivedVisitee, DerivedVisitee> {
}; // MoreDerivedVisitee
由您决定是否更喜欢简单的样板或智能(但可能令人困惑)的CRTP解决方案。
就个人而言,除非你有多个accept
的重载(通过在const-ness上重载最多4个),我不会打扰。手工编写accept
的工作量很大,而且很简单,也很容易理解。
答案 1 :(得分:0)
您应该将Visitee&
而不是DerivedVisitee&
作为visit
方法的参数传递。这是使用公共继承和多态性的重点。任何派生的对象都可以在需要 base 对象的地方使用。在运行时,将扣除实际类型,并在该对象上调用适当的方法。