Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Virtual inheritance and uniform initialization in C++

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:

  1. The code violates the standard in some way, making the outcome undefined (i.e., any outcome would be acceptable).
  2. One of the two compilers has a bug related to uniform initialization and multiple + virtual inheritance.

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?

like image 997
user1715611 Avatar asked Aug 16 '15 05:08

user1715611


People also ask

What is uniform initialization?

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.

What is virtual inheritance explain with example?

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.

Why do we initialize uniforms?

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.

How does virtual inheritance solve the diamond problem?

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.


1 Answers

List-initialization of an object or reference of type T is defined as follows:
(3.1) — If T is a class type and the initializer list has a single element of type cv U [..]
(3.2) — Otherwise, if T 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 and T 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.

like image 164
Columbo Avatar answered Sep 19 '22 17:09

Columbo