6

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?

Community
  • 1
  • 1
  • Sounds like a bug. No reason to think that there'd be UB here. – T.C. Aug 16 '15 at 06:00
  • VC++ 14.0 compiles it fine too, although IntelliSense complaines about it. –  Aug 16 '15 at 06:16
  • 2
    GCC is buggy when it comes to [aggregate-initialization of base classes in a mem-initializer](http://coliru.stacked-crooked.com/a/8c60d42406381caa). – David G Aug 16 '15 at 13:42
  • @0x499602D2 Interesting, it looks like it's trying to make a temporary and then do a copy. That'd also explain the OP's errors. – T.C. Aug 20 '15 at 08:34

1 Answers1

5

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.

T.C.
  • 133,968
  • 17
  • 288
  • 421
Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Thanks for pointing out the exact part of the standard describing this case. So it appears that the Intel compiler / "composer" takes its GCC compatibility somewhat too seriously, implementing GCC bugs as well. :-) As for bugs in the standard, I've recently spotted [this question about references and uniform initialization](http://stackoverflow.com/questions/10509603/why-cant-i-initialize-a-reference-in-an-initializer-list-with-uniform-initializ). Basically a transposition of 2 bullet points in the standard opened up a 3-year window of buggy (in a sense) compilers. – user1715611 Aug 17 '15 at 13:50