Without details of what the two type hierarchies' relation is with each other and how they interact, it's impossible to say what approach is appropriate. I've composed an overview of the other answers and another viable alternative that can be extended to the visitor pattern which was mentioned in a comment.
Performing the polymorphic behaviour in the children implementing a virtual
function in Parent
as already suggested by Joey Andres is quite typical object oriented solution for this problem in general. Whether it's appropriate, depends on the responsibilities of the objects.
The type detection as suggested by Olayinka and already mentioned in your question certainly smells kludgy, but depending on details, can be the minimum of N evils. It can be implemented with member function returning an enum
(I guess that's what Olayinka's answer tries to represent) or with a series of dynamic_cast
s as shown in one of the answers in the question you linked.
A trivial solution could be to overload foo
in Base
:
struct Base {
virtual void foo(const Parent&) = 0;
virtual void foo(const Child_Two&) = 0;
};
struct Derived_A: Base {
void foo(const Parent& p) {
// treat same
}
void foo(const Child_Two& p) {
foo(static_cast<Parent&>(p));
}
};
struct Derived_A: Base {
void foo(const Parent& p) {
// treat Child_One (and other)
}
void foo(const Child_Two& p) {
// treat Child_Two
}
};
If there are other subtypes of Base
that treat Child_One
and Child_Two
the same, then the implementation of foo(const Child_Two&)
may be put in Base
to avoid duplication.
The catch of this approach is that foo
must be called with a reference of proper static type. The call will not resolve based on the dynamic type. That may be better or worse for your design. If you need polymorphic behaviour, you can use the visitor pattern which essentially adds virtual dispatch on top of the solution above:
struct Base {
foo(Parent& p) {
p.accept(*this);
}
virtual void visit(Child_A&) = 0;
virtual void visit(Child_B&) = 0;
};
struct Parent {
virtual void accept(Base&) = 0;
};
struct Child_A: Parent {
void accept(Base& v) {
v.visit(*this);
}
};
// Child_B similarly
struct Derived_A: Base {
void treat_same(Parent&) {
// ...
}
void visit(Child_A& a) {
treat_same(a);
}
void visit(Child_B& b) {
treat_same(b);
}
};
struct Derived_B: Base {
void visit(Child_A&) {
// ...
}
void visit(Child_B&) {
// ...
}
};
There's a bit more boilerplate, but since you seem very averse to implementing the behaviour in the children, this may be good approach for you.