访问者和双重调度而不覆盖c ++

时间:2017-12-07 08:50:25

标签: c++ visitor double-dispatch

好的:这是我的问题:我有一个基础复合类,它接受一个访问者,然后迭代它的节点。奇迹般有效。 但是,我必须使用从这个composit派生的并且认识到我必须覆盖派生类中的“accept()”方法才能进行正确的双重调度(我不明白)。

这带来了两个缺陷:首先,我必须打破基础的隐藏结构,其次,我必须重复代码。要清除,这是我的伪代码:

struct Visitor
{
    void visit( BaseComposit*)    { throw( "not expected"); };
    void visit( DerivedComposit*) { throw( "ok"); };
};

class BaseComposit 
{
private:  

    std::vector< BaseComposit*> nodes;

public:

    virtual void accept( Visitor* v)
    {
        v->visit( this);

        for( int i = 0; i < nodes.size(); i++)
            nodes[ i]->accept( v);
    }
};

class DerivedComposit : public BaseComposit
{
public:
};

任何优雅的解决方案? 谢谢!

编辑:将“虚拟”添加到“接受()”以使其更精确......

2 个答案:

答案 0 :(得分:1)

  

任何优雅的解决方案?

不是真的。这是让访客模式有点痛苦的原因。虽然您可以借助模板缓解重复:

class BaseComposit 
{
private:  

    std::vector<BaseComposit*> nodes;

protected:

    template<class T>
    void accept_impl( Visitor* v, T* this_ )
    {
        v->visit( this_ );

        for(int i = 0; i < nodes.size(); i++)
            nodes[i]->accept(v);
    }

public:

    virtual void accept( Visitor* v ) { accept_impl( v, this ); }
};

现在accept必须产生的重复更小。

另外,正如@Oliv指出的那样,你的例子确实应该accept是一个虚函数。否则整件事都不会奏效。

如果你真的喜欢冒险,你可以引入一个宏来缓解&#34;注射&#34;每个班级accept。像这样:

#define INJECT_ACCEPT() void accept( Visitor* v ) override { accept_impl( v, this ); }

但是你仍然需要在每个派生类中使用它来使定义出现。

class DerivedComposit : public BaseComposit
{
public:
    INJECT_ACCEPT();
};

The semi-colon is allowed by a curious feature of the C++ grammar。所以我想有人可能会争论上面的看法&#34;自然&#34;。

答案 1 :(得分:1)

如果要强制某些代码始终执行,请使用非虚拟接口(NVI)模式:

class BaseComposit 
{
private:  
    std::vector<BaseComposit*> nodes;

    virtual void call_visit(Visitor* v) { v->visit(this); }

public:

    void accept(Visitor* v)
    {
        call_visit(v);

        for(int i = 0; i < nodes.size(); i++)
            nodes[i]->accept(v);
    }
};

class DerivedComposit : public BaseComposit
{
    void call_visit(Visitor* v) override { v->visit(this); }
public:
};