问题更像是理论问题.
前言.
访客模式:
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,因此在最好的情况下,我们将得到编译错误,甚至是错误的行为(如果Element会有一些重载的访问方法).
据我所知,问题在于尝试混合编译时和运行时功能.但可能存在一些基于模板的解决方案或新的C 11功能,还是其他什么?
一个注意事项:如果您不提供“宏魔术”:)的解决方案,我将不胜感激.
最佳答案 您可以使用
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.