从对基类的引用中调用更具体的重载函数
Calling the more-specific overloaded function from a reference to a base class
首先,对于这个神秘的标题,很抱歉,这不容易解释。
我想做的是使用重载函数在C++中实现访问者模式。这就是我的处境:
- 我有一组解析器。每个解析器返回一个特定的派生类型的
Element
。基本上,我最终得到了Element
s的多态集合,所有这些集合都实现了visit(Visitor&)
函数 - 我有一些分析仪(
Visitor
s)。每个访问者只对几个特定的Element
派生类感兴趣 Visitor
的基类具有visit(Element&)
的空实现,该实现接收作为参数的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);
}
这是输出
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上访问DerivedElement"。
这可能吗?看到C++没有实现多重调度,这似乎很困难,我可能在要求不可能的事情。然而,我真的很想看看我有什么替代方案,因为为我拥有的每个派生元素编写空的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,因为它很搞笑,但很糟糕。
建议
将你的帖子标题改为"如何利用访问者模式实现专业化"。
注意:您忘记在DerivedVisitor
类中使用virtual
关键字。
回答
您使用的两个功能不能很好地混合:
方法重写("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
}
};
方法过载。其中,来自相关类的方法方法和相同的id具有不同的参数。
class Visitor
{
public:
void visit(BaseElement& e)
{
// ...
}
};
class DerivedVisitor : public Visitor
{
public:
void visit(BaseElement& e)
{
// ...
}
void visit(DerivedElement& e)
{
// ...
}
};
由于您没有显示足够的源代码,我假设您将参数作为Element&
传递,即使它是作为DerivedElement&
创建的,所以编译器调用void visit(BaseElement& e)
,而不是void visit(DerivedElement& e)
。
我还注意到,您使用的是"引用",而不是指针。编译器可能将您的DerivedElement
强制转换为BaseElement
。我建议使用"指针"。
在使用多态性时,最好不要使用"方法重载",而是坚持使用"方法重写",因为编译器总是调用"虚拟"或"重写"方法。
class BaseElement
{
public:
virtual void accept(Visitor &v)
{
cout << "accept on BaseElement" << endl;
v.visit(*this);
}
virtual void doThings()
{
cout << "doThings on BaseElement" << endl;
}
};
class FooElement: BaseElement
{
public:
virtual void accept(Visitor &v)
{
cout << "accept on FooElement" << endl;
v.visit(*this);
}
virtual void doThings()
{
cout << "doThings on FooElement" << endl;
}
};
class BarElement: BaseElement
{
public:
virtual void accept(Visitor &v)
{
cout << "accept on BarElement" << endl;
v.visit(*this);
}
virtual void doThings()
{
cout << "doThings on BarElement" << endl;
}
};
class Visitor
{
public:
void visit(BaseElement* e)
{
cout << "visit Any Element on Visitor" << endl;
e.doThings();
}
};
class DerivedVisitor : public Visitor
{
public:
void visit(BaseElement* e)
{
cout << "visit Any Element on DerivedVisitor" << endl;
e.doThings();
}
};
int main (...)
{
BaseElement* e1 = new FooElement();
BaseElement* e2 = new BarElement();
BaseVisitor* v = new DerivedVisitor();
v->visit(e1);
v->visit(e2);
free v();
free e2();
free e1();
} // int main (...)
注:我跳过了大部分访问者操作,只是为了简化重载操作。
更新:通过方法重写(A.K.A."virtual"关键字),专门化在每个Element
类代码处,而不是在Visitor
类代码处。从每个派生类调用匹配方法。
干杯。
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 可以打印矢量和矢量中的矢量的非重载C++函数
- 错误 没有与参数列表匹配的重载函数"getline"实例
- 使用模板重载函数
- C++线程中,没有重载函数接受 X 参数
- std::vector 没有重载函数的实例与参数列表匹配
- C++重载函数,一个采用基类的参数,另一个采用派生类的参数
- 错误:无法解析对重载函数的引用;你的意思是调用它吗?
- 对重载函数find_first_not_of的不明确调用
- 如何从重载解析中删除重载函数?
- CUDA:重载函数"isnan"的多个实例
- C++派生类重载函数(带有 std::function 参数)不可见
- 避免在人为的重载函数调用中拼写出类型
- C++:如何为多个重载函数保留通用代码路径?
- 什么时候可以使用常量装饰调用我的重载函数?
- 尝试使用谓词函数会导致错误:"std::sort"未找到匹配的重载函数
- std::调用,未找到匹配的重载函数
- 为什么在传递长整型时调用具有两个双精度类型的参数的重载函数?
- 为什么使用不匹配的参数调用重载函数仍然有效
- 如何通过签名作为模板参数来解决重载函数?