简体   繁体   中英

Why does a user-provided default constructor lead to an uninitialized member?

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.

struct B has a user-provided constructor, so it doesn't get the default constructor that can initialize the members to zero. Your user-provided constructor replaces that, so you have to do the work yourself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM