Following up on this question about multiple (virtual) inheritance, I'd like to inquire about a simple MWE that makes g++ 5.2.0 upset whereas clang++ 3.6.2 handles it just fine, with no complaints at all, even with -Wall
and -Wextra
set. So here's the MWE:
class Z {};
class A : virtual Z { protected: A() {} };
class B : virtual Z { protected: B() {} };
class C : A, B { public: C() : A{}, B{} {} };
int main() { C c{}; return 0; }
Unlike clang++, g++ complains like this:
gccodd.c++: In constructor ‘C::C()’:
gccodd.c++:2:34: error: ‘A::A()’ is protected
class A : virtual Z { protected: A() {} };
^
gccodd.c++:4:39: error: within this context
class C : A, B { public: C() : A{}, B{} {} };
^
gccodd.c++:3:34: error: ‘B::B()’ is protected
class B : virtual Z { protected: B() {} };
^
gccodd.c++:4:39: error: within this context
class C : A, B { public: C() : A{}, B{} {} };
^
Replacing the uniform initialization in C's constructor with the old form works fine though and both clang++ and g++ are happy with the following:
class C : A, B { public: C() : A(), B() {} };
This yields the two obvious options:
If it were a matter of voting, (1) might win, because icpc 15.0.0 says the following:
gccodd.c++(4): error #453: protected function "A::A()" (declared at line 2) is not accessible through a "A" pointer or object
class C : public virtual A, public virtual B { public: C() : A{}, B{} {} };
^
gccodd.c++(4): error #453: protected function "B::B()" (declared at line 3) is not accessible through a "B" pointer or object
class C : public virtual A, public virtual B { public: C() : A{}, B{} {} };
^
So, is it (1) or (2)? And if it's the former case, then what's wrong with my MWE?
Uniform initialization is a feature in C++ 11 that allows the usage of a consistent syntax to initialize variables and objects ranging from primitive type to aggregates. In other words, it introduces brace-initialization that uses braces ({}) to enclose initializer values.
There are two vtable pointers, one per inheritance hierarchy that virtually inherits Animal . In this example, one for Mammal and one for WingedAnimal . The object size has therefore increased by two pointers, but now there is only one Animal and no ambiguity.
The uniform initialization is a feature that permits the usage of a consistent syntax to initialize variables and objects which are ranging from primitive type to aggregates. In other words, it introduces brace-initialization that applies braces ({}) to enclose initializer values.
The Diamond Problem is fixed using virtual inheritance, in which the virtual keyword is used when parent classes inherit from a shared grandparent class. By doing so, only one copy of the grandparent class is made, and the object construction of the grandparent class is done by the child class.
List-initialization of an object or reference of type
T
is defined as follows:
(3.1) — IfT
is a class type and the initializer list has a single element of type cvU
[..]
(3.2) — Otherwise, ifT
is a character array [..]
(3.3) — Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).
(3.4) — Otherwise, if the initializer list has no elements andT
is a class type with a default constructor, the object is value-initialized.
A
and B
both have base classes and hence aren't aggregates. So the fourth bullet point applies. And thus we have the exact same effect as if we had used ()
:
An object whose initializer is an empty set of parentheses, i.e.,
()
, shall be value-initialized.
Any compiler yielding different results with those initializers cannot be conforming.
§11.4, which handles access to protected
members, does not mention anything related to the form of initialization. However, concerning initialization of bases in a mem-initializer in the constructor, §11.4 is defective at the moment as mentioned by CWG issue #1883.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With