c – 重载和这个指针

问题更像是理论问题.

前言.

访客模式:

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.

点赞