Это продолжение к этот вопрос.
Мы можем реализовать шаблон посетителя для проблемы в предыдущем вопросе, как предложено в этот ответ:
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
классы.
Надеюсь, это немного прояснит ситуацию.
Я не совсем уверен, что получу то, что вы ищете, но я попробую.
Насколько я понимаю, вы хотите что-то вроде следующего в вашем коде:
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 для целей этого цикла.
К счастью, еще не все потеряно.
Вы можете сделать две вещи, обе из которых требуют определения «реального» типа ребенка во время выполнения.
Это требует чего-то вроде следующего.
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.
};
Другая альтернатива — использовать динамическое приведение. Что позволит включить 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.
};