问题更像是理论问题。
的前言
访客模式:
class Visitor
{
public:
virtual void VisitElementA(const ElementA& obj) = 0;
virtual void VisitElementB(const ElementB& obj) = 0;
};
class Element
{
public:
virtual void Accept(Visitor& visitor) = 0;
};
class ElementA : public Element
{
public:
void Accept(Visitor& visitor) override { visitor.VisitElementA(*this); }
};
class ElementB : public Element
{
public:
void Accept(Visitor& visitor) override { visitor.VisitElementB(*this); }
};
这个VisitElementA(const ElementA& obj)看起来有点难看,所以使用重载我们可以像这样重写它:
class Visitor
{
public:
virtual void Visit(const ElementA& obj) = 0;
virtual void Visit(const ElementB& obj) = 0;
};
现在我们在ElementA和ElementB中有两个相同的Accept方法实现:
void Accept(Visitor& visitor) override { visitor.Visit(*this); }
此类代码必须添加到ElementC,ElementD等(如果有的话)
问题是:如何避免这种重复?
将Accept实现放置在Element类(或其他一些中间类)中的天真解决方案将无法工作,因为此指针将指向对象类Element的对象,而不是ElementA或ElementB,因此在最好的情况下,我们将得到编译错误,甚至错误的行为(如果将有一些重载的元素的访问方法)。
据我所知,问题在于尝试混合编译时和运行时功能。但是可能存在一些基于模板的解决方案或新的C ++ 11功能,还是其他什么?
一个注意事项:如果您不提供“宏魔术”的解决方案,我将不胜感激:)。
答案 0 :(得分:2)
您可以使用CRTP模式。
将类Element
转换为模板类,该类将派生类型作为类型参数。然后,您可以在调用访问者之前向下转换为派生类型:
template <typename Derived>
class Element
{
public:
void Accept(Visitor& visitor) { visitor.Visit(*static_cast<Derived*>(this)); }
};
最后,每个具体元素都以这种方式来源于Element
:
class ElementA : public Element<ElementA>
{
};
另请注意,Accept(Visitor&)
不再需要是虚拟的。
<强>更新强> 以下是quetzalcoatl指出的问题的解决方案:
class ElementC : public Element<ElementC>, public ElementA
{
public:
using Element<ElementC>::Accept;
};
通过using声明,ElementC
将Accept
名称带入其范围,因此隐藏了基类中的名称。但是,此Accept
为Element<ElementC>::Accept
,实际上只隐藏了ElementA::Accept
。
答案 1 :(得分:0)
class Visitor
{
public:
virtual void Visit(const Element& obj) = 0;
}
class Element
{
public:
void Accept(Visitor& visitor) { visitor.Visit(*this); }
};
Edit2:您需要从Element基类调用Visit方法。因为Elements是多态的,所以你仍然传递了正确的对象。但是,如果需要访问元素的唯一方法或引入其他抽象方法,则可能需要强制转换。