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