【问题标题】:Inteface classes for generic application of visitor pattern访问者模式通用应用的接口类
【发布时间】:2014-08-24 15:20:33
【问题描述】:

我遇到了一个可以通过访问者模式解决的问题,因为我喜欢可重用的代码,所以我认为拥有一些已经为我完成大部分魔法并且以后可以重用的类可能是个好主意。所以我想要的是一个类似访问者类和访问者类的东西,我可以从中派生来准备我的派生类以使用访问者模式。我想出了这段代码:

template <typename ret = void>
class Visitor
{
public:
    typedef ret ReturnType;

protected:
    Visitor() {}
    ~Visitor() {}
};

template <typename BaseType>
class Visitable
{
public:
    template <typename Visitor>
    typename Visitor::ReturnType applyVisitor(Visitor& visitor)
    {
        return visitor(static_cast<BaseType*>(this));
    }

    template <typename Visitor>
    typename Visitor::ReturnType applyVisitor(Visitor& visitor) const
    {
        return visitor(static_cast<BaseType*>(this));
    }

protected:
    Visitable() {}
    ~Visitable() {}
};

template <typename VisitorType, typename VisitableType>
inline typename VisitorType::ReturnType applyVisitor(VisitorType visitor, VisitableType visitable)
{
    return visitable->applyVisitor(visitor);
}

class Base : public Visitable <Base>
{
public:
    virtual void foo() const
    {
        std::cout << "BASE" << std::endl;
    };

    std::string foobar() const
    {
        return "BASE";
    };
};

class Derived : public Base, public Visitable<Derived>
{
public:
    using Visitable<Derived>::applyVisitor;

    void foo() const
    {
        std::cout << "DERIVED" << std::endl;
    }; 

    std::string bar() const
    {
        return "DERIVED";
    };
};

struct MyVisitor : public Visitor < >
{
    template <class T>
    void operator()(T const var) const
    {
        var->foo();
    }
};

struct MyOtherVisitor : public Visitor <std::string>
{
    std::string operator()(Base * const var) const
    {
        return var->foobar();
    }

    std::string operator()(Derived * const var) const
    {
        return var->bar();
    }
};

int main(int _Argc, char* _Argv)
{
    Base *pVirtualDerived = new Derived();
    Base *pBase = new Base();
    Derived *pDerived = new Derived();

    std::cout << "Member method:" << std::endl;

    applyVisitor(MyVisitor(), pVirtualDerived);
    applyVisitor(MyVisitor(), pBase);
    applyVisitor(MyVisitor(), pDerived);

    std::cout << std::endl << "External method:" << std::endl;

    std::cout << applyVisitor(MyOtherVisitor(), pVirtualDerived) << std::endl;
    std::cout << applyVisitor(MyOtherVisitor(), pBase) << std::endl;
    std::cout << applyVisitor(MyOtherVisitor(), pDerived) << std::endl;
}

正如人们可能已经从名字中猜到的那样,我受到了 boost::static_visitorboost::variant 的启发。但是,也可以注意到我的实现在两个方面存在缺陷:

  1. 仅从 Visitable 继承是不够的,我还需要在我的类中添加 using 声明来解决 applyVisitor 方法的歧义。
  2. 这不是真正的访问者模式。使用实际上指向Derived 对象的Base* 调用applyVisitor 不会调用Derived::foo,而是调用Base::foo。我不能在Visitable&lt;T&gt; virtual 中声明applyVisitor,因为它是一个模板方法。但我需要模板,因为Visitor&lt;T&gt; 是它自己的模板类,我想为我的访问者保留通用返回类型。

长话短说,我能否以某种方式解决这两个问题并最终得到两个类,我只需要从中派生出两个类来为访问者模式准备我的代码吗?

【问题讨论】:

  • 它必须是您自己的实现还是您可以使用其他库? The Loki library 有一个通用的访问者实现。
  • 我在考虑 CRTP 是否可以成为解决方案,结果发现:shanhe.me/2011/08/06/…stackoverflow.com/a/7877397/104774 这些对您的问题有帮助吗?
  • 我已经在这里使用了 CRTP 技术,但是您链接中的示例代码仅支持固定返回类型,但我想支持任何返回类型。另一个例子似乎是基于 Loki 库的实现。所以我会看看这个。
  • 我不是因为 C++,而是因为这是一个访客问题。这是用 Java 完成的(希望这很有用):musingsofaprogrammingaddict.blogspot.ca/2009/01/…
  • @MatthiasB :不幸的是,Loki 库中的构造似乎不适用于我的情况。他们无法处理继承。

标签: c++ templates design-patterns interface visitor-pattern


【解决方案1】:

关于访问者设计模式和 C++ 继承有很多东西要讨论。在您描述的具体情况下,我认为解决方案是:

关于第一个问题,由于Base类已经继承了Visitable,所以在派生类中不需要再次继承:

class Derived : public Base
{
public:
    void foo() const
    {
        std::cout << "DERIVED" << std::endl;
    };
};

关于第二个问题,我想你只是忘记了 Base 类中的 virtual 关键字:

class Base : public Visitable<Base>
{
    public:
        virtual void foo() const
        {
            std::cout << "BASE" << std::endl;
        };
};

【讨论】:

  • 我的示例代码没有显示这个,所以我相应地更新了它,但我认为我需要两个类都从 Visitable 派生,以便在我需要调用的情况下(几乎) static_cast 工作不同的方法取决于实际的类型。由于这个原因,我的示例代码有点傻,因为我在那里所做的事情已经可以用简单的多态性来完成。
猜你喜欢
  • 2010-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 2022-12-04
  • 2014-06-11
相关资源
最近更新 更多