c++initializationvalue-initializationzero-initializationdefault-initialization

Does value-initialization first zero-initialize the members of object even with provided default member initializer in C++?


On value-initialization page cppreference states the following:

The effects of value-initialization are:

If T is a (possibly cv-qualified) class type:

  • If the default-initialization for T selects a constructor, and the constructor is not user-declared(until C++11)user-provided(since C++11), the object is first zero-initialized.
  • In any case, the object is default-initialized.

Question

Does it mean that in the following code the member x is first zero-initialized with value 0 and then default-initialized with value 10?

struct Foo
{
    int x = 10;
    int y;
};

Foo myfunc()
{
    return Foo(); // value-initialization, is x initialized with 0 first?
}

int main()
{
    myfunc();
    return 0;
}

I don't see any contradictions with what is stated on cppreference (nothing is said about default member initializer), so does it mean that there would be 2 memory writes to the address of x? If so, can we call the second write as initialization? Since initialization can occur only one time, I'd consider it as an assignment (x = 10).


Solution

  • Does it mean that in the following code the member x is first zero-initialized with value 0 and then default-initialized with value 10?

    Yes, except that the member x is not default-initialized. The whole class object is default-initialized in the second phase. But that calls the (implicitly-defined) default constructor which in turn initializes the member according to the default member initializer in int x = 10;, which is copy-initialization from the expression 10.

    Default-initialization of an int would leave its value unchanged. That's what is happening to y because it has no default member initializer, which means that the implicitly-defined default constructor will default-initialize it (in the second phase).

    so does it mean that there would be 2 memory writes to the address of x?

    It is a bit unclear in the C++ standard at the moment (at least as far as I can tell) whether the initialization (either of the two) counts as "write" (or rather, what the standard calls "modification"). See e.g. CWG issue 2587

    If you are asking about the compiled code, then there is almost surely not going to be two writes (with an optimizing compiler at least), because you never observe the zero-initialized state and so the program's observable behavior does not depend on whether or not two stores are emitted. Since emitting two stores would be pointless extra work, the compiler ought to optimize it to just one store.

    Although, in your specific example, the compiler is going to optimize the whole object away, because it has no observable effect at all.

    If so, can we call the second write as initialization?

    Both are (part of) initialization in the sense of the C++ standard.

    Since initialization can occur only one time, I'd consider it as an assignment (x = 10).

    The standard currently has multiple scenarios in which objects are initialized twice. That's only a problem in so far as that it is currently sometimes bit unclear in the specification whether the lifetime of an object already starts once the first (zero-)initialization is completed or only after the second initialization is completed. The currently proposed resolution is to consider lifetime to start after the first initialization, see CWG issue 2821.


    Here is a demonstration how you can observe the zero-initialization (assuming the above issue is resolved as suggested at least):

    #include<iostream>
    
    struct Foo
    {
        int x = (std::cout << x, 1);
    };
    
    int main()
    {
        auto foo = Foo();
        return 0;
    }
    

    This must print 0. Of course, nobody should write code like that, because if you e.g. write Foo foo; now instead, you get undefined behavior for accessing x outside its lifetime (or reading an indeterminate value), as Foo foo; is not value-initialization and therefore won't zero-initialize first.