шаблон посетителя для шаблонных производных классов

Связанный вопрос: ссылка на сайт.

В одном из ответов на поставленный выше вопрос мне было рекомендовано использовать шаблон посетителя для решения некоторых проблем, связанных с моей структурой наследования классов. Однако я не уверен, возможно ли использовать его в моем контексте, потому что мои производные классы могут быть нетиповыми шаблонами.

Чтобы продемонстрировать проблему, я использовал модифицированный код из этого источника: 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);
}

1

Решение

Проблема ваша 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 является требуется где в качестве указателя можно рассматривать необязательный.

2

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

Других решений пока нет …

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