Constructor crash using virtual inheritance and delegate constructors

struct D { virtual void m() const = 0; }; struct D1 : public virtual D { }; struct D2 : public virtual D { }; struct B : public D2 { B() { } B(int val) : B() { } void m() const { } }; struct A : public B, public D1 { A() : B(0) { } }; int main() { A a; return 0; } 

I am getting crashed with the MSVC 2013 compiler with the above code. It starts without fail when compiling with GCC 4.7.2. The class hierarchy is shown below.

  D / \ D1 D2 | | \ B \ / A 

Is this a mistake in the MS compiler, or did I make a mistake in the code?

+7
c ++ c ++ 11 virtual-inheritance visual-c ++ delegating-constructor
source share
2 answers

A quick analysis of the assembly code generated by the MSVC ++ 2013 compiler shows that the delegated call from B::B(int) to B() failed. This is a compiler error.

MSVC ++ constructors have a hidden logical parameter that tells the designer whether it is the construction of the derived object itself ( true ) or the built-in base subobject ( false ). In this example, only A::A() should receive true in this hidden parameter, while all calls to the lower-level constructor should receive false . However, when B() is called from B::B(int) , the compiler unconditionally passes 1 ( true ) as a hidden parameter. This is not true.

 ; Code for `B::B(int)` ... 00F05223 push 1 ; <- this is the problem 00F05225 mov ecx,dword ptr [this] 00F05228 call B::B (0F010F0h) ; <- call to `B::B()` ... 

In correctly generated code, when the compiler makes a delegated call to the constructor, it must pass by the value of the parameter received from the caller, and not hard-coded 1 .

The order of direct subconstructor calls made from A::A() in this example is as follows: 1) a common virtual base D , 2) base B , 3) base D1 .

In accordance with the rules of the language, in this case, constructor B and constructor D1 do not have to create their own virtual database D Base D already built at this point by the most derived object A This is exactly what is controlled by this hidden logical parameter. However, when B::B() is called from B::B(int) , the compiler passes an invalid parameter value (this is hardcoded 1 ), which is why B::B() incorrectly assumed that it was building the most derived object. This rotation makes B rebuild the shared virtual base D This override cancels the results of the correct construction already done by A::A() . This later crashes.

+6
source share

As far as I can tell, your sample code should work.

Aside, however, your constructor delegation may be considered bad practice. You must have one fully defined constructor that delegates an increasingly smaller specific constructor, and not vice versa. For example:

 struct B : public D2 { B() : B(0) { } B(int val) { } void m() const { } }; 
0
source share

All Articles