Я столкнулся с проблемой, которую можно решить с помощью шаблона посетителя, и, поскольку я люблю многократно используемый код, я подумал, что было бы неплохо иметь некоторые классы, которые уже делают для меня большую часть магии и которые я мог бы использовать позже. Так что я хотел что-то вроде класса 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;
}
Как можно догадаться по именам, которые меня вдохновили повышение :: static_visitor а также повышение :: вариант. Однако можно также заметить, что моя реализация имеет недостатки в двух аспектах:
applyVisitor
метод.applyVisitor
с Base*
что на самом деле указывает на Derived
объект не вызывает Derived::foo
но Base::foo
, Я не могу объявить applyVisitor
в Visitable<T>
виртуальный, потому что это шаблонный метод. Но мне нужен шаблон, потому что Visitor<T>
это сам шаблонный класс, и я хотел бы сохранить общий тип возвращаемого значения для моих посетителей.Короче говоря, могу ли я как-то решить обе проблемы и в итоге получить два класса, из которых мне просто необходимо извлечь свой код для шаблона посетителя?
Есть много вещей, чтобы поговорить о шаблонах проектирования посетителей и наследовании C ++. В конкретном случае, который вы описали, я думаю, что решения:
Что касается первой проблемы, поскольку базовый класс уже наследуется от Visitable, вам не нужно наследовать его снова в производном классе:
class Derived : public Base
{
public:
void foo() const
{
std::cout << "DERIVED" << std::endl;
};
};
Что касается второй проблемы, я думаю, вы просто забыли виртуальное ключевое слово в базовом классе:
class Base : public Visitable<Base>
{
public:
virtual void foo() const
{
std::cout << "BASE" << std::endl;
};
};