Q:

Multiple inheritance and polymorphism questions

Consider this C++ code:
#include <iostream>
using namespace std;

struct B {
    virtual int f() { return 1; }
    int g() { return 2; }
};
struct D1 : public B { // (*)
    int g() { return 3; }
};
struct D2 : public B { // (*)
    virtual int f() { return 4; }
};
struct M : public D1, public D2 {
    int g() { return 5; }
};

int main() {
    M m;
    D1* d1 = &m;
    cout << d1->f()
         << static_cast<D2&>(m).g()
         << static_cast<B*>(d1)->g()
         << m.g();
}

It prints 1225. If we make virtual inheritance, i.e. add virtual before public in lines marked with (*), it prints 4225.

Can you explain why 1 changes to 4?
Can you explain meaning of static_cast<D2&>(m) and static_cast<B*>(d1)?
How you are you not getting lost in this kind of combinations? Are you drawing something? 
Is it common to spot such complex settings in normal projects?

A:

Pictures speak louder than words, so before the answers...

Class M hierarchy WITHOUT virtual base inheritance of B for D1 and D2:
    M
   / \
  D1 D2
  |   |
  B   B

Class M hierarchy WITH virtual base inheritance of B for D1 and D2:
    M
   / \
  D1 D2
   \ /
    B

Cross-Delegation, or as I like to call it, sibling-polymorphism with a twist. The virtual base inheritance will fix up the  B::f() override to be D2:f(). Hopefully the picture helps explain this when you consider where the virtual functions are implemented, and what they override as a result of the inheritance chains.
the static_cast operator usage in this case drives conversion from derived-to-base class types.
Lots of experience reading really bad code and knowing how the underpinnings of the language 'work'
Thankfully no. It is not common. The original iostream libraries would have given you nightmares, though, if this is at-all confusing.

A:

Can you explain why 1 changes to 4?

Why does it change to 4? Because of cross-delegation.
Here's the inheritance graph before virtual inheritance:
B   B
|   |
D1  D2
 \ /
  M

d1 is a D1, so it has no idea that D2 even exists, and its parent (B) has no idea that D2 exists. The only possible result is that B::f() is called.
After virtual inheritance is added, the base classes are merged together.
  B
 / \
D1  D2
 \ /
  M

Here, when you ask d1 for f(), it looks to its parent. Now, they share the same B, so B's f() will be overridden by D2::f() and you get 4.
Yes, this is weird because it means that D1 has managed to call a function from D2, which is knows nothing about. This is one of the more odd parts of C++ and it is generally avoided.

Can you explain meaning of static_cast(m) and static_cast(d1)?

What don't you understand? They cast m and d1 to D2& and B* respectively.

How you are you not getting lost in this kind of combinations? Are you drawing something?

Not in this case. It's complicated, but small enough to keep in your head. I've drawn the graph in the above example to make things as clear as possible.

Is it common to spot such complex settings in normal projects?

No. Everyone knows to avoid the dreaded diamond pattern of inheritance because it's simply too complicated, and there's usually a simpler way to do whatever you want to do.
In general, it's better to prefer composition over inheritance.

A:

(1) Can you explain why 1 changes to 4?

Without virtual inheritance, there are 2 independent hierarchies of inheritance; B->D1->M and B->D2->M. So imagine 2 virtual function tables (though this is implementation defined).
When you invoke f() with D1*, it just knows about B::f() and that's it. With virtual inheritance, base class B is delegated to M and thus D2::f() is considered as part of class M.

(2) Can you explain meaning of static_cast<D2&>(m) and static_cast<B*>(d1)?

static_cast<D2&>(m), is like considering object of class M as class D2
static_cast<B*>(d1), is like considering pointer of class D1 as class B1.
Both are valid casts.
Since g() is not virtual the function choice happens at compile-time. Had it been virtual then all these casting won't matter.

(3) How you are you not getting lost in this kind of combinations? Are you drawing something?

Ofcourse it's complex and at first glance if there are so many of such classes, one might get easily lost.

(4) Is it common to spot such complex settings in normal projects?

Not at all, it's unusual and sometimes a code smell.

