【发布时间】: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_visitor 和 boost::variant 的启发。但是,也可以注意到我的实现在两个方面存在缺陷:
- 仅从 Visitable 继承是不够的,我还需要在我的类中添加 using 声明来解决
applyVisitor方法的歧义。 - 这不是真正的访问者模式。使用实际上指向
Derived对象的Base*调用applyVisitor不会调用Derived::foo,而是调用Base::foo。我不能在Visitable<T>virtual 中声明applyVisitor,因为它是一个模板方法。但我需要模板,因为Visitor<T>是它自己的模板类,我想为我的访问者保留通用返回类型。
长话短说,我能否以某种方式解决这两个问题并最终得到两个类,我只需要从中派生出两个类来为访问者模式准备我的代码吗?
【问题讨论】:
-
它必须是您自己的实现还是您可以使用其他库? 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