Does the Default Constructor Initialize Built-In Types

Is it guaranteed that defaulted constructor initialize built in types automatically to 0?

Test = default will default initialize its members.
but for type as int or float, default initialization is different than value-initialization

so

Test t; // t.s and t.m have unitialized value

whereas

Test t{}; // t.s == 0 and t.m == 0.0f;

Do built-in types have default constructors?

A constructor is a member function (constructors are fully specified in clause 12 of the C++ Standard, which covers special member functions like constructors and destructors).

A member function can only be defined for a class type (C++03 9.3/1 says "Functions declared in the definition of a class, excluding those declared with a friend specifier, are called member functions of that class").

So non-class types (including fundamental types, array types, reference types, pointer types, and enum types) do not have constructors.

I don't have a copy of The C++ Programming Language to read the context of the quote that "Built-in types also have default constructors," but I would guess that Stroustrup is either using the term "constructor" in a loose, non-technical sense, or the meaning of the term or the way in which it is used in the Standard changed between when the book was published and when the language was standardized. I'd guess the former is far more likely than the latter.

Are members of a built-in type ever default-initialised?

First let's go over some examples and the correct terminology.

T a1;            // default initialization
T a2{}; // value initialization
T(); // also value initialization
new T; // default initialization
new T(); // value initialization
new T{}; // also value initialization
class C1 {
C1() {}
T x;
}; // no initializer for C1::x; default-initialized
class C2 {
T x;
}; // implicit default constructor default-initializes C2::x
class C3 {
C3() : x() {}
T x;
}; // C3::x will be value-initialized.
class C4 {
C4() : x{} {}
T x;
}; // C4::x will also be value-initialized.
// DANGER
T a(); // declares a function; not value initialization (quirk of C++)

Generally, the rule is that when there is no initializer, it is default initialization, and when the initializer is () or {}, it is value initialization. Note that there is an exception for statics and thread-locals, which I'll discuss later.

For an integer or floating-point type, value initialization sets it to 0. For a pointer type, value initialization sets it to null. Default initialization does nothing for scalar types. Therefore, if an object of scalar type only receives default initialization, then it has indeterminate value.

a) Does initialisation of members of a built-in type have anything to do with wheher a user-defined constructor is defined or not,

The default constructor for a class default-initializes members. A member is also default-initialized when no mem-initializer is explicitly provided for it. The examples C1 and C2 illustrate this. However, note that when a class type is value-initialized, and the class's default constructor is either implicitly defined or explicitly defaulted, the class's members will be zeroed out. This zeroing out occurs only in this case, and doesn't occur for a user-provided default constructor. So the answer to your question is "yes" in this sense.

C1 y1;   // y1 is default-initialized; y1.x is indeterminate
C1 y2{}; // y2 is value-initialized; y2.x is indeterminate
C2 y3; // y3 is default-initialized; y3.x is indeterminate
C2 y4{}; // y4 is value-initialized; y4.x is set to 0
C3 y5; // y5 is default-initialized; y5.x is set to 0
C3 y6{}; // y6 is value-initialized; y6.x is set to 0

b) do members of a built-in type always need to be initialised manually, and c) are there any circumstances under which an object's storage is zeroed-out before the constructor is called?

I assume you mean "class members with built-in type". I covered a case above in which they are automatically initialized to 0: where the class object is value-initialized and its constructor is not user-provided (or deleted). Another case is when the class object has static or thread-local storage duration. In this case, the members will also be zeroed out at the very beginning, so there is no chance of them ending up with indeterminate value.

Does the defaulted default constructor initialize variables to zero?

Is setting the constructor to default the equivalent of doing:

MyClass() : var{}, ptr{}, array{}, data{}, smart_ptr{} {}

No. It is not.


The line

MyClass() = default;

is more akin to but not exactly equivalent to:

 MyClass() {}

In either case, using

 MyClass obj;

results in a default-initialized object whose members are default initialized.

However, the difference between them when using

 MyClass obj{};

is that obj will be zero-initialized with the defaulted default constructor while it will be still default initialized with the user provided default constructor.

Does a default constructor always initialize all members?

Quoting C++11:

5.2.3 Explicit type conversion (functional notation) [expr.type.conv]

2 The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type,which is value-initialized (8.5; no initialization is done for the void() case). [...]

8.5 Initializers [dcl.init]

7 To value-initialize an object of type T means:

  • ...
  • 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.
  • ...

So in C++11, S().a should be zero: the object is zero-initialized before the constructor gets called, and the constructor never changes the value of a to anything else.

Prior to C++11, value initialization had a different description. Quoting N1577 (roughly C++03):

To value-initialize an object of type T means:

  • ...
  • if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
  • ...
  • otherwise, the object is zero-initialized

Here, value initialization of S did not call any constructor, but caused value initialization of its a and b members. Value initialization of that a member, then, caused zero initialization of that specific member. In C++03, the result was also guaranteed to be zero.

Even earlier than that, going to the very first standard, C++98:

The expression T(), where T is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, whose value is determined by default-initialization (8.5; no initialization is done for the void() case).

To default-initialize an object of type T means:

  • if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
  • ...
  • otherwise, the storage for the object is zero-initialized.

So based on that very first standard, VC++ is correct: when you add a std::string member, S becomes a non-POD type, and non-POD types don't get zero initialization, they just have their constructor called. The implicitly generated default constructor for S does not initialise the a member.

So all compilers can be said to be correct, just following different versions of the standard.

As reported by @Columbo in the comments, later versions of VC++ do cause the a member to be initialized, in accordance with more recent versions of the C++ standard.

Writing a Default Constructor Forces Zero-Initialization?

Once you provide a constructor for a type, it will always be
invoked, both for default initialization and for value
initialization. It's a fundamental principle of the language.
So once you define Foo::Foo(), it will be called any time you
construct a Foo; if there is a default constructor, it will be
invoked, even in the case of default initialization. So the
behavior you are seeing is correct.

EDIT:

Default initialization is explained §8.5/7, in particular:

To default-initialize an object of type T means:

— if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called
[...]

In your case, you'll probably also want to look at how the
compiler generates a default constructor if none is provided,
§12.1/4; in particular, the generated default constructor
invokes the default constructor of any base classes or members.

Value initialization is in §8.5/8. It is basically default
initialization preceded by zero initialization, so that default
initialization that doesn't do anything still finds everything
zero initialized.

More fundamentally, however: in this case, a very fundamental
principle of C++ is involved, dating to long before the first
standard: if you provide a constructor for an object, it will
be used. Without doing all sorts of strange pointer casts, it
is impossible to get an object without it being properly
constructed. The standard describes how this occurs, and covers
a lot of other special cases, but the basic principle has been
there from the start (and any proposal which would cause it not
to be respected in the standard is bound to fail).

What Form Does the Implicitly-Declared Default Constructor Take?

What you're experiencing is called default initialization and the rules for it are (emphasis mine):

  • if T is a non-POD (until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;
  • if T is an array type, every element of the array is default-initialized;
  • otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.

Edit, in response to OP's request below:

Note that declaring a constructor = default does not change the situation (again, emphasis mine):

Implicitly-defined default constructor

If the implicitly-declared default constructor is not defined as deleted, it is defined (that is, a function body is generated and compiled) by the compiler if odr-used, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list. That is, it calls the default constructors of the bases and of the non-static members of this class.

Since the default constructor has an empty initializer list, its members satisfy the conditions for default initialization:

Default initialization is performed in three situations:

...

3) when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.

Also note that you have to be careful when experimentally confirming this, because it's entirely possible that the default-initialized value of an int may be zero. In particular, you mentioned that this:

struct Foo {
Foo(){};
int member;
} foo;

Results in value-initialization, but it does not; here, member is default-initialized.

Edit 2:

Note the following distinction:

struct Foo {
int member;
};

Foo a; // is not value-initialized; value of `member` is undefined
Foo b = Foo(); // IS value-initialized; value of `member` is 0

This behavior can be understood by following the rules for value-initialization:

Value initialization is performed in these situations:

1,5) when a nameless temporary object is created with the initializer consisting of an empty pair of parentheses;

Form 1 (T();) is the form used on the right-hand side of the = above to initialize b.

The effects of value initialization are:

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;

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;

3) if T is an array type, each element of the array is value-initialized;

4) otherwise, the object is zero-initialized.

Finally though, note that in our earlier example:

struct Foo {
Foo(){}; // this satisfies condition (1) above
int member;
};

Foo f = Foo();

Now, condition (1) applies, and our (empty) user-declared constructor is called instead. Since this constructor does not initialize member, member is default-initialized (and its initial value is thus undefined).

C++: initialization of int variables by an implicit constructor

For built-in types like int, the implicit default constructor does nothing (no initialization).

This is true but it is also not. Default initialization results in an unitialized object, while value initialization doesn't.

Why are these cases different?

1. C++11, 8.5/11

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value.

If you use int i; this results in an uninitialized integer!

2. C++11, 8.5/10

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

If you use int i = int(); you have a value-initialized i. Now, what is value-initialized?

3. C++11, 8.5/7

To value-initialize an object of type T means:

  • [...] (some options where T may be class or array type)
  • otherwise, the object is zero-initialized.

Ok now we know that int i = int(); means having i=0.

Since your struct is POD, value-initializing it means value-initializing all of its members.

You can have a shortcut on the general behaviour

int i1, i2 = int();
std::cout << i1 << std::endl;
std::cout << i2 << std::endl;

If the memory where i1 resides, isn't zero by any luck you can have the output will probably be

somevalue
0

[As @jogojapan mentioned correctly: Reading from i1 is undefined in first place, so don't do it. You'll most likely observe what I describe here but since the standard doesn't enforce compilers to behave this way i1 may be zero, or brake the expected result in any other strange way.]

Be aware of the following:

Note: Since () is not permitted by the syntax for initializer,

X a();


is not the declaration of a value-initialized object of class X, but the declaration of a function taking no argument and returning an X.

Emphasis on standard quotes are mine.



Related Topics



Leave a reply



Submit