4

这个问题更像是理论上的。

前言。 访客模式:

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 会有一些重载的 Visit 方法)。

据我了解,问题在于尝试混合编译时和运行时功能。但可能存在一些基于模板的解决方案或新的 C++11 功能,还是其他?

请注意:如果您不提供“宏魔法”的解决方案,我将不胜感激:)。

4

2 回答 2

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隐藏的。

于 2013-03-23T08:45:52.360 回答
0
class Visitor
{
public:
    virtual void Visit(const Element& obj) = 0;
}
class Element
{
public:
    void Accept(Visitor& visitor) { visitor.Visit(*this); }
};

Edit2:您只需要从 Element 基类调用 Visit 方法。因为元素是多态的,所以您仍然传递了正确的对象。但是,如果您需要访问元素独有的方法或引入其他抽象方法,则可能需要强制转换。

于 2013-03-23T08:10:23.543 回答