Why does brace initialization assignment fill variables with garbage?

a = {}; is an assignment, a is assigned from a temporary object constructed from {}. The implicitly-generated assignment will perform member-wise assignment on all the data members, then the point would be how the temporary object is initialized from {}.

This is copy-list-initialization, as the effect, value-initialization is performed.

Otherwise, If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed.

As the effect of value-initialization,

1) if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;

A has a user-provided default constructor, and as the effect of default initialization, that default constructor is used to initialize the temporary object. The user-provided default constructor's body is empty, then for the temporary object, var4 will default-initialized by std::string's default constructor, all the other data members with build-in type will have indeterminate values.

  1. If I get rid of default constructor - the problem goes away.

Then the behavior of value-initialization will change to

(emphasis mine)

2) if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;

Note the difference here, the temporary object will be zero-initialized at first. Then all the data members with build-in type are initialized to 0 (var4 is still default-initialized).

  1. If class's member variable is brace-initialized, then it will be assigned to 0 or default value.

This is how default initializer list works.

Through a default member initializer, which is simply a brace or equals initializer included in the member declaration, which is used if the member is omitted in the member initializer list

Then all the data members are initialized by the specified initializer; in your sample they're all value-initialized, as the effect, var4 is default-initialized, other members are zero-initialized to 0.


a = {};

This line does not mean that all the variables inside the class get an {} initializer. It instead calls a (not defined, so automatically generated) copy (or move) assignment operator which does a shallow copy/move from an object created with {} (i.e. with uninitialized variables) to the object you have.

var4 seems to be cleared, but it's actually copied/moved from the new object var4, and since std::string has a default constructor, it's empty.

The simple solution to avoid stuff like that is to initialize your non-class variables inside the class, say

class A
{
    int var = 0;
    ...

};