Please consider the following code:
#include <iostream>
struct A{ // with implicit default constructor
int number;
};
struct B{
int number;
B(){}; // user-provided default constructor
};
int main()
{
A aa = {};
B bb = {};
std::cout << "aa.number: " << aa.number << std::endl;
std::cout << "bb.number: " << bb.number << std::endl;
}
Running the code online results in the following output:
aa.number: 0
bb.number: 19715
Why is bb.number uninitialized? I thought that zero initialisation is guaranteed by using ={} ?
I thought that zero initialisation is guaranteed by using ={} ?
That is only true if the type is "correct", which B
is not. B bb = {};
will default construct a B
and your default constructor, B(){};
, doesn't initialize number
so no matter what, number
will never be initialized because that is how your default constructor works. If you had a "correct" constructor like
B() : number(0) {};
// or use
int number = 0;
B(){};
Then you'd get zero initialization of number
when it is default constructed.
This isn't the case with A
because A
is an aggregate and those come with certain guarantees like zero initialization if an empty braced-init-list, technical name for the {}
, is used.
A
is an aggregate type, because it doesn't have any user-provided constructors (and fulfills a few other requirements).
Therefore A aa = {};
does not call the implicitly generated default constructor. Instead initialization with brace-enclosed initializer lists performs aggregate initialization, which for an empty list means that the members are initialized as if by a {}
initializer, which in turn means for a member of scalar type such as int
that it will be initialized to zero.
B
is not an aggregate type, because it does have a user-provided constructor.
Therefore B bb = {};
cannot do aggregate initialization and will call the default constructor instead. The default constructor (in either A
or B
) does not specify an initializer for the members and so the member is default-initialized, which for a fundamental type, such as int
, means that it will not be set to any value. Its value will remain indeterminate.
Accessing the indeterminate value, which your program does, causes undefined behavior.
If you declare a constructor yourself, then it becomes that constructor's responsibility to initialize all the members appropriately. The rule that = {}
or {}
always initializes only holds under the assumption that a user-provided default constructor, if it exists, does the right thing in the sense that it provides sensible initializers to all its members and it doesn't have to mean zero-initialization necessarily.
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