C++ 从对基类的引用调用更具体的重载函数
首先,很抱歉这个神秘的标题,这不容易解释 我想做的是用超载函数在C++中实现访问者模式。这就是我的情况:C++ 从对基类的引用调用更具体的重载函数,c++,overriding,overload-resolution,visitor,C++,Overriding,Overload Resolution,Visitor,首先,很抱歉这个神秘的标题,这不容易解释 我想做的是用超载函数在C++中实现访问者模式。这就是我的情况: 我有一个解析器集合。每个解析器返回一个特定的派生类型的元素。基本上,我最终得到了元素的多态集合s,它们都实现了visit(Visitor&)功能 我有一些分析器(Visitors)。每个访问者只对几个特定的元素派生类感兴趣 Visitor的基类有一个空的visit(Element&)实现,它接收一个元素引用作为参数 每个Visitor派生类为其感兴趣的特定元素类型实现visit函数。也就是说
元素
。基本上,我最终得到了元素的多态集合
s,它们都实现了visit(Visitor&)
功能Visitor
s)。每个访问者只对几个特定的元素派生类感兴趣
Visitor
的基类有一个空的visit(Element&)
实现,它接收一个元素
引用作为参数Visitor
派生类为其感兴趣的特定元素类型实现visit
函数。也就是说,我在DerivedVisitor
类中有一个visit(DerivedElement&)
重载函数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);
}
标有(!)的行是我想更改的行。这些行应该是“在DerivedVisitor上访问DerivedElement”
这可能吗?看到C++没有实现它,我似乎很难做到。然而,我真的很想看看我有什么选择,因为为我拥有的每个派生元素编写空的
accept(DerivedElementN&)
方法似乎不是最好的选择。建议
将文章标题改为“如何实现访问者模式的专门化”
注意:
您忘记在DerivedVisitor
类中使用virtual
关键字
回答
您使用的两个功能混合不好:
方法重写(“虚拟”)。其中,来自相关类且id相同的方法具有相同的参数
class Visitor
{
public:
virtual void visit(BaseElement& e)
{
// print element value to console
}
};
class DerivedVisitor : public Visitor
{
public:
virtual void visit(BaseElement& e)
{
// save element value into file
}
};
class Visitor
{
public:
void visit(BaseElement& e)
{
// ...
}
};
class DerivedVisitor : public Visitor
{
public:
void visit(BaseElement& e)
{
// ...
}
void visit(DerivedElement& e)
{
// ...
}
};
方法重载。其中,来自相关类且id相同的方法具有不同的参数
class Visitor
{
public:
virtual void visit(BaseElement& e)
{
// print element value to console
}
};
class DerivedVisitor : public Visitor
{
public:
virtual void visit(BaseElement& e)
{
// save element value into file
}
};
class Visitor
{
public:
void visit(BaseElement& e)
{
// ...
}
};
class DerivedVisitor : public Visitor
{
public:
void visit(BaseElement& e)
{
// ...
}
void visit(DerivedElement& e)
{
// ...
}
};
由于您没有显示足够的源代码,因此我假设您将参数作为元素传递&
,即使它是作为DerivedElement&
创建的,因此编译器调用void-visit(BaseElement&e)
,而不是void-visit(DerivedElement&e)
我还注意到,您使用的是“引用”,而不是指针。编译器可能会将您的DerivedElement
强制转换为BaseElement
。我建议用“指针”来代替
在使用多态性时,最好不要使用“方法重载”,并坚持使用“方法重写”,因为编译器总是调用“虚拟”或“重写”方法
类基本元素
{
公众:
虚拟无效接受(访客和验证)
{
cout您在这里做了大量的动态间接操作。因此,您也需要以这种方式构建您的访问者
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
}
};
struct BaseVisitor{
std::无序的_映射类型;
模板无效添加重载(F){
类型[typeid(D)]=[=](基本元素和元素){
f(静态_-cast(elem));
};
}
虚拟无效访问(BaseElement和elem){
if(types.find(typeid(elem))!=types.end()
类型[类型ID(元素)](元素);
}
};
结构派生访问者:BaseVisitor{
DerivedVisitor(){
AddOverload([](派生元素和e){
});
//…等等
}
};
核心问题是,只要你需要动态间接寻址,你就不能使用模板。你所能做的就是在垃圾邮件动态\u cast
上提供额外的一层类型安全和方便(以及潜在的速度)
作为一个简短的说明,上面的代码可能不会很好地工作-typeid对引用或const
或类似的东西有一些乐趣,它们可能会导致类型查找在成功时失败
如果对您来说很重要的话,还可以应用其他技术来消除这一限制,但您可能希望坚持使用dynamic_cast,因为它很有趣但很糟糕。问题是,这与我提出的不同。我需要您的示例没有提供的专门化。我想添加一个不同的实现nDerivedVisitor
用于DerivedElements
s,但这不起作用。此外,问题是visit
元素不是从main
函数中调用的,而是从BaseElement/DerivedElement
类中的accept
方法中调用的,这增加了更多的难度。@gjulianm检查对我答案的更新。每个访问者只有一个visit
方法,accept
方法是虚拟的,并且在每个派生类或专门化类中都“重写”。但是它仍然有相同的问题。如果调用e1->accept(*v)
,当accept
接收到BaseVisitor
参数时,它将只调用接收BaseElement
的常规visit
方法
方法在DerivedVisitor
类中,它不会从accept
mehod调用,这就是为什么。也就是说,我实际上想混合方法重载和重写。@Gjulian对不起,你真的像示例那样修改和编译了吗,或者你支持不起作用吗?我会把这个示例带到我的家用电脑上,在多态性中,如果将参数传递给cl