8
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 get crash with MSVC 2013 compiler with above code. It runs without crash when compiled with GCC 4.7.2. Hierarchy of classes is depicted below.

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

This is a bug in MS compiler or I made a mistake in the code?

eXXXXXXXXXXX2
  • 1,540
  • 1
  • 18
  • 32

2 Answers2

6

A quick examination of the assembly code generated by MSVC++ 2013 compiler shows that the delegated call from B::B(int) to B() is made incorrectly. It is a bug in the compiler.

MSVC++ constructors have a hidden boolean parameter that tells the constructor whether it is constructing a most derived object (true) or an embedded base subobject (false). In this example, only A::A() should receive true in this hidden parameter, while all lower-level constructor calls should receive false. However, when B() is called from B::B(int), the compiler unconditionally passes 1 (true) as that hidden parameter. This is incorrect.

; 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 the the properly generated code when the compiler makes a delegated constructor call, it should pass along the parameter value received from the caller, not a hardcoded 1.

The order of immediate sub-constructor calls made from A::A() in this example is as follows: 1) common virtual base D, 2) base B, 3) base D1.

Per rules of the language, in this case the constructor of B and the constructor of D1 is not supposed to construct their virtual base D. Base D is already constructed at that point by the most derived object A. This is exactly what is controlled by that hidden boolean parameter. However, when B::B() is called from B::B(int), the compiler passes an incorrect parameter value (that hardcoded 1), which caused B::B() to incorrectly assume that it constructing a most derived object. This is turn makes B re-construct the common virtual base D. This re-construction overrides the results of the proper construction already made by A::A(). Later this causes the crash.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
0

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

As an aside, though, your constructor delegation might be considered bad practice. You should have one fully defined constructor that all lesser defined constructor delegate to, not the other way around. For example:

struct B : public D2
{
    B() : B(0) { }

    B(int val) { }

    void m() const { }
};
tawnos178
  • 761
  • 5
  • 3