Virtual inheritance vs. non-default constructors

Virtual inheritance vs. non-default constructors

Hi.I was wondering if the compiler trying to generate a fully blown default constructor for B here (as opposed to skipping A::A(), which it still does, but only at runtime) is correct, or a bug (sinceB cannot be instantiated, anyway, and its descendants will have to construct A themselves).

class A {

public:

	A( int ) {  }

};
class B: virtual public A {

public:

	//B(): A( -1 ) {  } // uncomment to make it compilable

	virtual void do_something() = 0;

};
class C: public B {

public:

	C(): A( 1 ) {  }

	virtual void do_something() {  }

};
int main() {

	C c;

	return 0;

}

Results:
1>tests.cpp(6): error : no default constructor exists for class "A"

1>    class B: virtual public A {

1>                            ^

1>            detected during implicit generation of "B::B()" at line 14

----Regards,Vladimir

7 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hello Vladimir,

the code you provided does not follow the standard, the compiler is right:
A virtual base class needs to be initialized in (every!) most derived class's constructor list.
Means: Both classes B & C need to construct the possible sub-object of class A, even though there will be only one reasonable way (via class C).

Rationale:
C++ does not know the concept of an interface. You can use classes with pure virtual methods to achieve a similar result. However, for C++ it's still a sub-object which needs to be constructed.

You already proposed a possible solution. Another way would be to create a default constructor for class A and assert during run-time that it's never been used. AFAIK it cannot be asserted during compilation time because of the missing interface concept (distinction between ordinary class/object and interface not possible). A trivial implementation could look like this:

#include 
class A {

    public:

        A() { assert(0); };

        A( int ) { }

};
class B: virtual public A {

    public:

        virtual void do_something() = 0;

};
class C: public B {

    public:

        C() : A(1) { }

        virtual void do_something() {  }

};
int main() {

    C c;

    return 0;

}

Best regards,

Georg Zitzlsberger

Quoting Georg Zitzlsberger (Intel)
A virtual base class needs to be initialized in (every!) most derived class's constructor list.
Means: Both classes B & C need to construct the possible sub-object of class A, even though there will be only one reasonable way (via class C).

Thank you, Georg.
Are you referring to 12.6.2/10? "First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized [...]" AFAICT, the passage from 1.8 (1.8/4) mentioned here actually excludes B from the list of potential most derived classes as it can only be a subobject, not a complete object (as per 10.4/2): "If a complete object, a data member (9.2), or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject". Since B cannot be the most derived class, it does not need to construct A at all, isn't it so?

Even if B could be constructed by itself, doesn't the compiler violate12.6.2/10 by requiring A::A() to be present for calling from B::B() _while compiling C::C()_ (note the line number in the error message)---only to skip this call at runtime?

I'm having a hard time finding passages in the standard that make compilers require the presence of constructors they could not possibly call (putting aside the possibility of constructing A by itself, of course). Constructing A via C is not just the only reasonable way---it's the only possible way, since B is abstract (this notion being specified in the standard, even though there's no interface concept).

That said, there certainly are a number of ways to circumvent the issue, and I will probably use one of them. However, comments on the standard compliance will still be greatly appreciated.----Regards,Vladimir

Quoting Georg Zitzlsberger (Intel)
the code you provided does not follow the standard, the compiler is right

Asked and answered athttp://stackoverflow.com/a/11693977/1558356. In essence: the code does not follow the standard, but the compiler is wrong. The problem is not the missing A::A(), the problem is missing B::B(). The latter must be deleted (not generated) according to 12.1/5 "A defaulted default constructor for class X is defined as deleted if [...] any [...] virtual base class [...] has class type M [...] and [...] M has no default constructor [...]."

Quoting vpozdyayevQuoting Georg Zitzlsberger (Intel)the code you provided does not follow the standard, the compiler is right

Asked and answered athttp://stackoverflow.com/a/11693977/1558356. In essence: the code does not follow the standard,
but the compiler is wrong. The problem is not the missing A::A(), the problem is missing B::B(). The latter must be deleted
(not generated) according to 12.1/5 "A defaulted default constructor for class X is defined as deleted if [...] any [...] virtual
base class [...] has class type M [...] and [...] M has no default constructor [...]."

Hi Vladimir,I have a question:

Are you doing R&D on how to breaksome C++ compiler(s)?

I would like to note that if a project needs to beVERYportablea developerneeds to follow more than 20-year-old C++ standards.
In that case a complex C++ projectcould be compiled even with a Turbo C++compiler v3.x ( an example of a legacy technolody ).

A C++ constructor is needed to construct an object. It is an Essense of OOP.If you don'twant to call a constructor,
in case of a high-performance applications of your codes in a Real-Time environment, than you could create
an instance of the object with a CRT 'malloc' function ( but your class needs to be a simple one / there are limitations / etc).

Here is an example:
...
class A
{
public:
A(){};
~A(){};
void Init(){ /* some initialization on demand when it is needed*/ };
};
...
A *pA[ 65536 ] = { NULL };
for( int i=0; i<65536; i++)
{
pA[i]=( A * )malloc( sizeof( A ) );
}
...
pA[12345]->Init(); // Initialization of object's members some time later
...

That trick is used in a high-performance Strassen's Heap-Based algorithm for matrix multiplication optimized for embedded platforms.

Best regards,
Sergey

Thanks, Sergey.No, I'm not really trying to break the compiler, although this seems to be exactly what happens every now and then lately ;) . Just trying to make it better---and being a bit of a language lawyer here, I guess. As for the legacy standards, my project isR&D, actually, so it's not limited by them.----Regards,Vladimir

Quoting vpozdyayevThanks, Sergey. No, I'm not really trying to break the compiler, although this seems to be exactly what happens every now and then lately ;) . Just trying to make it better---and being a bit of a language lawyer here, I guess. As for the legacy standards, my project isR&D, actually...

It is never a bad thing when a person tries to verify some standards! Thank you for the response.
Best regards,
Sergey

Login to leave a comment.