首先,对不起这个神秘的头衔,这很难解释。
我想做的是使用重载函数在C++中实现Visitor模式。这是我的情况:
Element
的特定派生类型。基本上,我最终得到了Element
的多态集合,它们全部实现了visit(Visitor&)
函数。 Visitor
)。每个访问者仅对几个特定的Element
派生类感兴趣。 Visitor
的基类有一个空的visit(Element&)
实现,该实现将Element
引用作为自变量。 Visitor
派生类都为其感兴趣的特定元素类型实现visit
函数。也就是说,在visit(DerivedElement&)
类中有一个DerivedVisitor
重载函数。 accept(Visitor& v) { v.visit(*this); }
时,所调用的函数应该是更具体的函数。也就是说,如果v
是DerivedVisitor
,并且accept
是在DerivedElement
中实现的,则希望调用visit(DerivedElement&)
函数。 一些示例代码:
#include <iostream>
using namespace std;
class Visitor
{
public:
virtual void visit(class BaseElement& e);
};
class BaseElement
{
public:
virtual void accept(Visitor &v)
{
cout << "accept on BaseElement" << endl;
v.visit(*this);
}
virtual void doThings()
{
cout << "doThings on BaseElement" << endl;
}
};
void Visitor::visit(BaseElement& e)
{
cout << "visit on Visitor" << endl;
e.doThings();
}
class DerivedElement : public BaseElement
{
public:
virtual void accept(Visitor &v)
{
cout << "accept on DerivedElement" << endl;
v.visit(*this);
}
virtual void doThings()
{
cout << "doThings on DerivedElement" << endl;
}
};
class DerivedVisitor : public Visitor
{
public:
void visit(BaseElement& e)
{
cout << "visit-BaseElement on DerivedVisitor" << endl;
e.doThings();
}
void visit(DerivedElement &e)
{
cout << "visit-DerivedElement on DerivedVisitor" << endl;
e.doThings();
}
};
int main(int argc, char** argv)
{
BaseElement eBase;
DerivedElement eDeriv;
BaseElement& eDerivAsBase = eDeriv;
Visitor vBase;
DerivedVisitor vDeriv;
cout << "Visiting a BaseElement with the base visitor:" << endl;
eBase.accept(vBase);
cout << endl << "Visiting a BaseElement with the derived visitor:" << endl;
eBase.accept(vDeriv);
cout << endl << "Visiting Base and Derived elements with the derived visitor" << endl;
eBase.accept(vDeriv);
eDeriv.accept(vDeriv);
cout << endl << "Visiting Base element as Derived reference" << endl;
eDerivAsBase.accept(vBase);
eDerivAsBase.accept(vDeriv);
}
这是输出
Visiting a BaseElement with the base visitor:
accept on BaseElement
visit on Visitor
doThings on BaseElement
Visiting a BaseElement with the derived visitor:
accept on BaseElement
visit-BaseElement on DerivedVisitor
doThings on BaseElement
Visiting Base and Derived elements with the derived visitor
accept on BaseElement
visit-BaseElement on DerivedVisitor
doThings on BaseElement
accept on DerivedElement
visit-BaseElement on DerivedVisitor (!)
doThings on DerivedElement
Visiting Base element as Derived reference
accept on DerivedElement
visit on Visitor
doThings on DerivedElement
accept on DerivedElement
visit-BaseElement on DerivedVisitor (!)
doThings on DerivedElement
标有(!)的行是我要更改的行。这些行应为“DerivedVisitor上的visit-DerivedElement”。
这可能吗?看到C++没有实现multiple dispatch似乎很困难,我可能正在寻求不可能的实现。但是,我真的很想看看我有什么选择,因为为我拥有的每个派生元素编写空的
accept(DerivedElementN&)
方法似乎不是最佳选择。 最佳答案
您在这里做了很多动态间接。因此,您也将需要以这种方式构造访客。
struct BaseVisitor {
std::unordered_map<std::type_info, std::function<void(BaseElement&)>> types;
template<typename D, typename F> void AddOverload(F f) {
types[typeid(D)] = [=](BaseElement& elem) {
f(static_cast<D&>(elem));
};
}
virtual void visit(BaseElement& elem) {
if (types.find(typeid(elem)) != types.end())
types[typeid(elem)](elem);
}
};
struct DerivedVisitor : BaseVisitor {
DerivedVisitor() {
AddOverload<DerivedElement>([](DerivedElement& e) {
});
//... etc
}
};
核心问题是,只要需要这种动态间接,就不能使用模板。您所能做的就是在垃圾邮件
dynamic_cast
上提供一层额外的类型安全性和便利性(并可能提高速度)。简要说明一下,上面的代码可能无法正常工作-使用typeid来引用或
const
或从某些东西中获取乐趣,可能会导致类型查找在应成功时失败。如果对您很重要,则可以应用其他技术来消除该限制,但是您可能要坚持使用dynamic_cast,因为它很有趣但很糟糕。
关于c++ - 从对基类的引用中调用更具体的重载函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29565524/