Классы интерфейса для общего применения шаблона посетителя

Я столкнулся с проблемой, которую можно решить с помощью шаблона посетителя, и, поскольку я люблю многократно используемый код, я подумал, что было бы неплохо иметь некоторые классы, которые уже делают для меня большую часть магии и которые я мог бы использовать позже. Так что я хотел что-то вроде класса 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 а также повышение :: вариант. Однако можно также заметить, что моя реализация имеет недостатки в двух аспектах:

  1. Недостаточно просто наследовать от Visitable, мне также нужно поместить объявление using в мой класс, чтобы устранить неоднозначность для applyVisitor метод.
  2. Это не совсем шаблон для посетителей. призвание applyVisitor с Base* что на самом деле указывает на Derived объект не вызывает Derived::foo но Base::foo, Я не могу объявить applyVisitor в Visitable<T> виртуальный, потому что это шаблонный метод. Но мне нужен шаблон, потому что Visitor<T> это сам шаблонный класс, и я хотел бы сохранить общий тип возвращаемого значения для моих посетителей.

Короче говоря, могу ли я как-то решить обе проблемы и в итоге получить два класса, из которых мне просто необходимо извлечь свой код для шаблона посетителя?

1

Решение

Есть много вещей, чтобы поговорить о шаблонах проектирования посетителей и наследовании 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;
};
};
0

Другие решения


По вопросам рекламы [email protected]