重载和这个指针

时间:2013-03-23 07:58:45

标签: c++ c++11 this overloading

问题更像是理论问题。

前言 访客模式:

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功能,还是其他什么?

一个注意事项:如果您不提供“宏魔术”的解决方案,我将不胜感激:)。

2 个答案:

答案 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声明,ElementCAccept名称带入其范围,因此隐藏了基类中的名称。但是,此AcceptElement<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是多态的,所以你仍然传递了正确的对象。但是,如果需要访问元素的唯一方法或引入其他抽象方法,则可能需要强制转换。