You might want to look at Loki TypeLists if you really need to be able to track ancestry and enumerate through types. I'm not sure if what you are asking for is really possible without a bunch of work. Make sure that you aren't over-engineering here.
On a slightly different note, if you are going to use MI in this manner (i.e., the dreaded diamond), then you should be very explicit about which virtual member you want. I can't think of a good case where you want to choose the semantics of B::fn()
over C::fn()
without explicitly making a decision when writing D
. You will probably pick one over the other (or even both) based on what the individual method does. Once you have made a decision, the requirement is that inherited changes do not change the expectations or semantic interface.
If you are really worried about swapping in a new class, say E
in place of say B
where E
does not descend from B
but offers the same interface, then you should really use the template approach though I'm not sure why there is a static_cast<>
in there...
struct A {
virtual ~A() {}
virtual void f() = 0;
};
struct B: A {
virtual void f() { std::cout << "B::f()" << std::endl; }
};
struct C: A {
virtual void f() { std::cout << "C::f()" << std::endl; }
};
template <typename Base1, typename Base2>
struct D: Base1, Base2 {
void g() { Base1::f(); Base2::f(); }
};
int main() {
D<B,C> d1;
D<C,B> d2;
d1.g();
d2.g();
return 0;
}
// Outputs:
// B::f()
// C::f()
// C::f()
// B::f()
works fine and seems a little easier to look at.