Связанный вопрос: ссылка на сайт.
В одном из ответов на поставленный выше вопрос мне было рекомендовано использовать шаблон посетителя для решения некоторых проблем, связанных с моей структурой наследования классов. Однако я не уверен, возможно ли использовать его в моем контексте, потому что мои производные классы могут быть нетиповыми шаблонами.
Чтобы продемонстрировать проблему, я использовал модифицированный код из этого источника: http://sourcemaking.com/design_patterns/visitor/cpp/2.
Приведенный ниже пример не компилируется, потому что невозможно определить метод виртуального шаблона. Однако, я считаю, код демонстрирует то, чего я пытаюсь достичь. Есть ли альтернативные варианты решения проблемы?
// 1. Add an accept(Visitor) method to the "element" hierarchy
class Element
{
public:
virtual void accept(class Visitor &v) = 0;
};
template <unsigned int N>
class This: public Element
{
public:
/*virtual*/void accept(Visitor &v);
string thiss()
{
return "This";
}
};
class That: public Element
{
public:
/*virtual*/void accept(Visitor &v);
string that()
{
return "That";
}
};
// 2. Create a "visitor" base class w/ a visit() method for every "element" type
class Visitor
{
public:
template<unsigned int N>
virtual void visit(This<N> *e) = 0;
virtual void visit(That *e) = 0;
};
template<unsigned int N>
/*virtual*/void This<N>::accept(Visitor &v)
{
v.visit(this);
}
/*virtual*/void That::accept(Visitor &v)
{
v.visit(this);
}
// 3. Create a "visitor" derived class for each "operation" to do on "elements"class UpVisitor: public Visitor
{
/*virtual*/void visit(This *e)
{
cout << "do Up on " + e->thiss() << '\n';
}
/*virtual*/void visit(That *e)
{
cout << "do Up on " + e->that() << '\n';
}
};
class DownVisitor: public Visitor
{
/*virtual*/void visit(This *e)
{
cout << "do Down on " + e->thiss() << '\n';
}
/*virtual*/void visit(That *e)
{
cout << "do Down on " + e->that() << '\n';
}
};
int main()
{
Element *list[] =
{
new This<3>(), new That()
};
UpVisitor up; // 4. Client creates
DownVisitor down; // "visitor" objects
for (int i = 0; i < 2; i++) list[i]->accept(up);
for (int i = 0; i < 2; i++) list[i]->accept(down);
}
Проблема ваша Visitor
класс тесно связан с классами, производными от Element
, По мере того как вы расширяете свой дизайн, это будет мешать больше, чем уже есть. Вы можете уменьшить / исключить правильное связывание, предоставив класс «destination», который определяет все требования посещаемого объекта. Поскольку имя производного класса является общим атрибутом, вы можете поместить хранилище и получить к нему доступ также в целевой класс.
// 1. Define out visitor and destination interfaces
struct Destination
{
Destination(const std::string& name) : name_(name) {}
virtual std::string ident() const { return name_; }
const std::string name_;
};
struct Visitor
{
virtual void visit(Destination *e) = 0;
};
Это позволяет отделить требования посетителя от класса Element, что, по-видимому, является вашим намерением. Тогда ваш This
а также That
классы наследуются от Destination
и предоставить необходимые реализации.
// 2. Define our element and it's derived classes
class Element
{
public:
virtual void accept(class Visitor &v) = 0;
};
template <unsigned int N>
class This: public Element, public Destination
{
public:
This() : Destination("This") {}
virtual void accept(Visitor &v)
{
v.visit(this);
}
};
class That: public Element, public Destination
{
public:
That() : Destination("That") {}
virtual void accept(Visitor &v)
{
v.visit(this);
}
};
Теперь ваши посетители вверх и вниз упрощены в нечто вроде следующего
// 3. Create a "visitor" derived class for each "operation" to do on "elements"class UpVisitor: public Visitor
{
void visit(Destination *e) {
cout << "do Up on " + e->ident() << '\n';
}
};
class DownVisitor: public Visitor
{
void visit(Destination *e) {
cout << "do Down on " + e->ident() << '\n';
}
};
Хотя я не изменил его в решении выше, я рекомендую изменить visit
взять указатель вместо указателя. Поскольку C ++ не имеет понятия нулевой ссылки, это означает, что Destination
является требуется где в качестве указателя можно рассматривать необязательный.
Других решений пока нет …