Я столкнулся с проблемой, которую можно было решить с помощью шаблона посетителя, и, поскольку я люблю многократно используемый код, я подумал, что было бы неплохо иметь несколько классов, которые уже делают большую часть магии для меня и которые я мог бы повторно использовать позже. Итак, я хотел что-то вроде класса Visitor и класса Visitable, из которого я мог бы подготовить производный класс для использования шаблона посетителя. Я придумал этот код:
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
. - На самом деле это не шаблон посетителя. Вызов
applyVisitor
сBase*
, который на самом деле указывает на объектDerived
, вызывает неDerived::foo
, аBase::foo
. Я не могу объявитьapplyVisitor
вVisitable<T>
виртуальном, потому что это шаблонный метод. Но мне нужен шаблон, потому чтоVisitor<T>
сам по себе является классом шаблона, и я хотел бы сохранить общий тип возвращаемого значения для моих посетителей.
Короче говоря, могу ли я каким-то образом решить обе проблемы и в итоге получить два класса, от которых мне просто нужно получить производные, чтобы подготовить мой код для шаблона посетителя?