This is specifically regarding C++11:
#include <iostream> struct A { A(){} int i; }; struct B : public A { int j; }; int main() { B b = {}; std::cout << b.i << b.j << std::endl; } Compiling with g++ 8.2.1:
$ g++ -std=c++11 -pedantic-errors -Wuninitialized -O2 a.cpp a.cpp: In function ‘int main()’: a.cpp:25:25: warning: ‘b.B::<anonymous>.A::i’ is used uninitialized in this function [-Wuninitialized] std::cout << b.i << " " << b.j << std::endl gcc is detecting b.i as uninitialized, but I would think it should be getting zero-initialized along with b.j.
What I believe is happening (C++11 specifically, from the ISO/IEC working draft N3337, emphasis mine):
B is not an aggregate because it has a base class. Public base classes were only allowed in aggregates in C++17.A is not an aggregate because it has a user-provided constructorSection 8.5.1
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
b is getting list initialized with an empty braced-init-listSection 8.5.4
List-initialization of an object or reference of type T is defined as follows:
— If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
— Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).
b gets value-initializedB has an implicitly-defined default constructor, so b value-initialization invokes zero-initializationb.B::A gets zero-initialized, which zero-initalizes b.B::A.i, and then b.B::j gets zero-initialized. Section 8.5
To zero-initialize an object or reference of type T means:
...
— if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
...
To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
However, it looks like gcc is saying only b.B::j is going to get zero-initialized. Why is this?
One reason I can think of is if B is being treated as an aggregate, which would initialize b.B::A with an empty list. B is certainly not an aggregate, though, because gcc rightly errors if we try to use aggregate initialization.
// ... as in the above example int main() { B b = {A{}, 1}; std::cout << b.i << " " << b.j << std::endl; } Compiling with C++11
$ g++ -std=c++11 -pedantic-errors -Wuninitialized -O2 a.cpp a.cpp: In function ‘int main()’: a.cpp:10:18: error: could not convert ‘{A(), 1}’ from ‘<brace-enclosed initializer list>’ to ‘B’ B b = {A{}, 1}; Compiling with C++17
g++ -std=c++17 -pedantic-errors -Wuninitialized -O2 a.cpp a.cpp: In function ‘int main()’: a.cpp:11:25: warning: ‘b.B::<anonymous>.A::i’ is used uninitialized in this function [-Wuninitialized] std::cout << b.i << " " << b.j << std::endl; And we can see that b.i is uninitialized because B is an aggregate, and b.B::A is getting initialized by an expression that itself leaves A::i uninitialized.
So it's not an aggregate. Another reason is if b.B::j is getting zero-initialized, and b.B::A is getting value-initialized, but I don't see that anywhere in the specs.
The last reason is if an older version of the standard was getting invoked. From cppreference:
2) if T is a non-union class type without any user-provided constructors, every non-static data member and base-class component of T is value-initialized; (until C++11)
In this case, both b.B::i and b.B::A would be value-initialized, which would cause this behavior, but that is marked as "(until C++11)".
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