This is a simplification of some real code, and a real mistake I made when I didn't realize someone else had already implemented Foo and derived from it.
#include <iostream>
struct Base {
virtual ~Base() { }
virtual void print() = 0;
};
struct OtherBase {
virtual ~OtherBase() { }
};
struct Foo : public Base { // better to use virtual inheritance?
virtual void print() { std::cout << "Foo" << std::endl; };
};
struct Bar : public Base { // better to use virtual inheritance?
virtual void print() { std::cout << "Bar" << std::endl; };
};
// The design is only supposed to implement Base once, but I
// accidentally created a diamond when I inherited from Bar also.
class Derived
: public OtherBase
, public Foo
, public Bar // oops.
{
};
int main() {
Derived d;
OtherBase *pO = &d;
// cross-casting
if (Base *pBase = dynamic_cast<Base *>(pO))
pBase->print();
else
std::cout << "fail" << std::endl;
}
EDIT: to save you from having to run this code...
- If run as-is, it prints "fail" (undesireable, hard to debug).
- If you delete the line marked "oops" it prints "Foo" (desired behavior).
- If you leave the "oops" and make the two inheritances virtual, it won't compile (but at least you know what to fix).
- If you delete the "oops" and make them virtual, it will compile and will print "Foo" (desired behavior).
With virtual inheritance, the outcomes are either good or compiler-error. Without virtual inheritance, the outcomes are either good or unexplained, hard-to-debug runtime failure.
When I implemented Bar, which basically duplicated what Foo was already doing, it caused the dynamic cast to fail, which meant bad things in the real code.
At first I was surprised there was no compiler error. Then I realized there was no virtual inheritance, which would have triggered the 'no unique final overrider' error in GCC. I purposefully chose not to use virtual inheritance since there aren't supposed to be any diamonds in this design.
But had I used virtual inheritance when deriving from Base, the code would have worked just as well (without my oops), and I would have been warned about the diamond at compile time vs. having to track down the bug at run time.
So the question is -- do you think it's acceptable to use virtual inheritance to prevent making a similar mistake in the future? There's no good technical reason (that I can see) for using virtual inheritance here, since there should never be a diamond in the design. It would only be there to enforce that design constraint.