class — Реализация шаблона посетителя для вектора объектов в Stack Overflow

Это продолжение к этот вопрос.

Мы можем реализовать шаблон посетителя для проблемы в предыдущем вопросе, как предложено в этот ответ:

class Base {
foo(Parent& p) {
p.accept(*this);
}
virtual void visit(Child_A&) = 0;
virtual void visit(Child_B&) = 0;
};

class Parent {
virtual void accept(Base&) = 0;
};

class Child_A: Parent {
void accept(Base& v) {
v.visit(*this);
}
};

class Child_B: Parent {
void accept(Base& v) {
v.visit(*this);
}
};

class Derived_A: Base {
void treat_same(Parent&) {
// ...
}
void visit(Child_A& a) {
treat_same(a);
}
void visit(Child_B& b) {
treat_same(b);
}
};
class Derived_B: Base {
void visit(Child_A&) {
// ...
}
void visit(Child_B&) {
// ...
}
};

Но теперь рассмотрим, если foo ожидает std::vector<std::shared_ptr<Parent>> const& в качестве аргумента.
Тогда как мы можем реализовать шаблон посетителя для проблемы? Является ли это возможным?

РЕДАКТИРОВАТЬ

foo проходит std::vector<std::shared_ptr<Parent>> const& в другой класс, state, Но если все объекты в векторе имеют тип Child_A это вызывает state.method_A, если все объекты в векторе имеют тип Child_B это вызывает state.method_B а иначе звонит state.method_C, Только state работает напрямую с parent классы.

Надеюсь, это немного прояснит ситуацию.

0

Решение

Я не совсем уверен, что получу то, что вы ищете, но я попробую.

Насколько я понимаю, вы хотите что-то вроде следующего в вашем коде:

std::vector<std::shared_ptr<Parent>> children;
Base * handler = new Derived_A; // or new Derived_B.
for (child : children) {
// You want this call to correctly distinguish
// between objects of Child_A and Child_B
handler->visit(child);

К сожалению, C ++ не * позволяет вам это делать. (* продолжайте читать) Поскольку вызовы функций определяются типом, и все дочерние элементы имеют тип shared_ptr для целей этого цикла.

К счастью, еще не все потеряно.


Вы можете сделать две вещи, обе из которых требуют определения «реального» типа ребенка во время выполнения.

Вариант 1. Добавьте поле в Parent, определяющее, как оно должно обрабатываться.

Это требует чего-то вроде следующего.

class Parent {
public:
enum class SubType {
A,
B,
};

virtual void accept(Base &) = 0;

// Subclasses must implement this to
// allow instances of base to correctly handle the objects.
virtual SubType handle_as() const = 0;
};

Реализация Base будет включать что-то вроде следующего:

class Base {
void visit( shared_ptr<Parent> p ) {
switch( p->handle_as() ) {
case Parent::SubType::A:
this->accept( *static_ptr_cast<Child_A>(p) );
break;
case Parent::SubType::B:
this->accept( *static_ptr_cast<Child_B>(p) );
break;
}
// In addition to your accept(Child_A &) accept(Child_B &) etc.
};

Вариант 2. Использование идентификации типа RunTime. (RTTI).

Другая альтернатива — использовать динамическое приведение. Что позволит включить RTTI во всем вашем исполняемом файле, это может быть тем, что вам нужно в этом случае, но имейте в виду, что это влечет за собой небольшие затраты производительности + размер исполняемого файла.

dynamic_cast вернет nullptr, если приведение является недопустимым, в противном случае он вернет действительный указатель.

Короче:

class Base {
void visit( shared_ptr<Parent> p ) {
if ( dynamic_ptr_cast<Child_A>(p) ) {
this->accept( *dynamic_ptr_cast<Child_A>(p) );
}
else if ( dynamic_ptr_cast<Child_B>(p) ) {
this->accept( *dynamic_ptr_cast<Child_B>(p) );
}
}
// In addition to your accept(Child_A &) accept(Child_B &) etc.
};
0

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


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