Reputation:
Consider the code:
#include <iostream>
using std::cout;
using std::endl;
struct A
{
virtual void foo(){ };
A()
{
cout << "A()" << endl;
}
};
struct B : virtual A
{
virtual void bar() = 0;
B() : A() //mem-initializer of virtual base class
{
cout << "B()" << endl;
}
};
struct C : B
{
void bar(){ };
C() : B()
{
cout << "C()" << endl;
}
};
C c;
int main()
{
//A()
//B()
//C()
//Is output
}
I've written the code to understand the rule's note from 12.6.2/8 N37973:
[Note: An abstract class (10.4) is never a most derived class, thus its constructors never initialize virtual base classes, therefore the corresponding mem-initializers may be omitted. — end note]
If we omit mem-initializer in the B()
and make the base class A
as non-virtual we'll have the same result.
So what purpose of the note that I cited?
Upvotes: 2
Views: 1054
Reputation: 320381
Your example doesn't demonstrate much, since the virtual base A
has a default constructor. You never have to explicitly invoke default constructor for a virtual base class (or any base class for that matter). The compiler will invoke that default constructor implicitly everywhere it is needed, regardless of whether you've done it explicitly yourself.
Remove the default constructor and provide a constructor with a parameter in A
struct A
{
A(int)
{
cout << "A()" << endl;
}
};
The note in question is telling you that now you will have to explicitly invoke the constructor for base A
from each non-abstract class virtually derived from A
(directly or indirectly). In your case you have to explicitly initialize A
from the constructor of C
struct C : B
{
void bar(){ }
C() : A(42)
{
cout << "C()" << endl;
}
};
since C is not an abstract class.
But you don't have to initialize A
from the constructor of B
, since B
is an abstract class. I.e. this
struct B : virtual A
{
virtual void bar() = 0;
B()
{
cout << "B()" << endl;
}
};
should compile in C++11, even tough the "classic" (pre-C++11) language rules required an explicit initialization of A
from the constructor of B
as well.
Note that GCC compiler used by ideone (for one example), still reports an error for the above definition of class B
even in C++11 mode. Apparently, it has not been updated yet to follow that rule of C++11.
Upvotes: 1
Reputation: 137310
It's talking about something like this:
#include <iostream>
using std::cout;
using std::endl;
struct A
{
virtual void foo(){ };
// N.B.: no default ctor
A(int)
{
cout << "A(int)" << endl;
}
};
struct B : virtual A
{
virtual void bar() = 0;
B()
{
cout << "B()" << endl;
}
};
struct C : B
{
void bar(){ };
C() : A(10), B()
{
cout << "C()" << endl;
}
};
C c;
int main()
{
}
If A
weren't a virtual base of B
, or B
weren't abstract, then B::B()
must have an appropriate mem-initializer for A
; however, since B
is abstract and A
is a virtual base, and B
's constructor will never actually construct A
, the mem-initializer can be omitted. Note that g++ currently does not implement this rule and still requires a mem-initializer for B
(which it will never actually use). Clang does, however. Demo.
See also CWG issue 257, which introduced this rule, with the phrasing further altered later by CWG issue 1658. The relevant GCC bug reports are bug 19249 and 53878.
Upvotes: 4
Reputation: 119069
If B
inherits non-virtually from A
, then B
's constructor must initialize the A
base class subobject. Since A
has a default constructor, you don't have to explicitly mention A
in the ctor-initializer for B
. However, if A
did not have a default constructor, and B
inherited non-virtually from A
, you would have to explicitly initialize A
in the ctor-initializer for B
.
If B
inherits virtually from A
, and B
is an abstract class, then even if A
has no default constructor, you still don't need to mention A
in the ctor-initializer for B
. This is because B
's constructor will never have the responsibility of initializing the A
subobject; instead, the most derived class's constructor is the one that has to initialize the A
subobject, since A
is a virtual base class.
Upvotes: 2