Zero-Initialize array member in initialization list - c++

I have a class with an array member that I would like to initialize to all zeros.
class X
{
private:
int m_array[10];
};
For a local variable, there is a straightforward way to zero-initialize (see here):
int myArray[10] = {};
Also, the class member m_array clearly needs to be initialized, as default-initializing ints will just leave random garbage, as explained here.
However, I can see two ways of doing this for a member array:
With parentheses:
public:
X()
: m_array()
{}
With braces:
public:
X()
: m_array{}
{}
Are both correct? Is there any difference between the two in C++11?

Initialising any member with () performs value initialisation.
Initialising any class type with a default constructor with {} performs value initialisation.
Initialising any other aggregate type (including arrays) with {} performs list initialisation, and is equivalent to initialising each of the aggregate's members with {}.
Initialising any reference type with {} constructs a temporary object, which is initialised from {}, and binds the reference to that temporary.
Initialising any other type with {} performs value initialisation.
Therefore, for pretty much all types, initialisation from {} will give the same result as value initialisation. You cannot have arrays of references, so those cannot be an exception. You might be able to construct arrays of aggregate class types without a default constructor, but compilers are not in agreement on the exact rules. But to get back to your question, all these corner cases do not really matter for you: for your specific array element type, they have the exact same effect.

The types of initialization can be kind of tedious to go through, but in this case it is trivial. For:
public:
X()
: m_array()
{}
since the expression-list between the parentheses are empty, value-initialization occurs. Similarly for:
public:
X()
: m_array{}
{}
list-initialization occurs, and subsequently value-initialization since the brace-init-list is empty.
To give a more comprehensive answer, let's go through §8.5 of N4140.
If no initializer is specified for an object, the object is
default-initialized. When storage for an object with automatic or
dynamic storage duration is obtained, the object has an indeterminate
value, and if no initialization is performed for the object, that
object retains an indeterminate value until that value is replaced
(5.17).
This indeterminate value is what you refer to as garbage values.
To zero-initialize an object or reference of type T means:
— if T is an array type, each element is zero-initialized
To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type ... then the object is default-initialized; ...
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized.
The semantics of initializers are as follows. ...
— If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
— If the initializer is (), the object is value-initialized.
So far it's clear that value initialization will make each element of the array zero since int is not a class type. But we have not yet covered list initialization and aggregate initialization, since an array is an aggregate.
§8.5.4:
List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate, aggregate initialization is performed (8.5.1).
And back to §8.5.1:
If there are fewer initializer-clauses in the list than there
are members in the aggregate, then each member not explicitly
initialized shall be initialized from its brace-or-equal-initializer
or, if there is no brace-or-equal-initializer, from an empty
initializer list (8.5.4).
And we end with §8.5.4 again:
List-initialization of an object or reference of type T is defined as follows:
— Otherwise, if the initializer list has no elements, the object is value-initialized.
Since traversing the (draft) standard can take breath out of you, I recommend cppreference as it breaks it down pretty good.
Relevant links:
cppreference:
aggregate initialization
value initialization
Draft standard:
N4140

Parentheses work in C++98, and are calling for zero initialization, which is what you want. I verified on gcc 4.3. Edit: removed incorrect statement about C++11. I also confirmed that empty braces perform empty-list-initialization using clang 3.4 with -std=c++11.

Related

Are there any difference in empty parentheses (“T()”) and empty braces (“T{}”) when used as initializers?

Generally speaking, parentheses and braces are very different. For minimal reproducible example:
#include <array>
#include <vector>
int main()
{
std::array<int, 2>{42, 42}; // OK
std::array<int, 2>(42, 42); // ill-formed
std::vector<int>{42, 42}; // two elements
std::vector<int>(42, 42); // 42 elements
}
However, since empty braces use value-initialization instead of std::initializer_list constructors, is there any different between empty parentheses and empty braces when used as initializers?
More formally, given a type T, is it possible that T() and T{} are different? (Either may be ill-formed.)
(This question and answer was originally created for C++20 standard compatible vector on Code Review Stack Exchange. It is intended that the answer covers all possible cases. Please inform me if I missed any.)
(The links in this answer point to N4659, the C++17 final draft. However, at the time of this writing, the situation is exactly the same for C++20.)
Yes, it's possible. There are two cases:
Case 1
T is a non-union aggregate for which zero-initialization, followed by default-initialization if the aggregate has a non-trivial constructor, differs from copy-initialization from {}.
We can use std::in_place_t to construct our example, because it has an explicit default constructor. Minimal reproducible example:
#include <utility>
struct A {
std::in_place_t x;
};
int main()
{
A(); // well-formed
A{}; // ill-formed
}
(live demo)
Case 1, variant
T is a union aggregate for whose first element default-initialization differs from copy-initialization from {}.
We can change struct to union in Case 1 to form a minimal reproducible example:
#include <utility>
union A {
std::in_place_t x;
};
int main()
{
A(); // well-formed
A{}; // ill-formed
}
(live demo)
Case 2
T is of the form const U& or U&& where U can be list-initialized from {}.
Minimal reproducible example:
int main()
{
using R = const int&;
R{}; // well-formed
R(); // ill-formed
}
(live demo)
Detailed explanation
T()
Per [dcl.init]/17:
The semantics of initializers are as follows. The destination type
is the type of the object or reference being initialized and the
source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the
source type is not defined.
If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is list-initialized.
If the destination type is a reference type, see [dcl.init.ref].
If the destination type is an array of characters, an array of char16_­t, an array of char32_­t, or an array of wchar_­t, and
the initializer is a string literal, see [dcl.init.string].
If the initializer is (), the object is value-initialized.
[...]
We can conclude that T() always value-initializes the object.
T{}
Per [dcl.init]/17:
The semantics of initializers are as follows. The destination type
is the type of the object or reference being initialized and the
source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the
source type is not defined.
If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is
list-initialized.
[...]
That's enough for us to conclude that T{} always list-initializes the object.
Now let's go through [dcl.init.list]/3. I have highlighted the possible cases. The other cases are not possible because they require the initializer list to be non-empty.
List-initialization of an object or reference of type T is defined
as follows:
(3.1) If T is an aggregate class and the initializer list has a single element of type cv U, where U is T or a class derived from
T, the object is initialized from that element (by
copy-initialization for copy-list-initialization, or by
direct-initialization for direct-list-initialization).
(3.2) Otherwise, if T is a character array and the initializer list has a
single element that is an appropriately-typed string literal
([dcl.init.string]), initialization is performed as described in that
section.
(3.3) Otherwise, if T is an aggregate, aggregate initialization is
performed.
(3.4) Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is
value-initialized.
(3.5) Otherwise, if T is a specialization of
std​::​initializer_­list<E>, the object is constructed as described
below.
(3.6) Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best
one is chosen through overload resolution ([over.match],
[over.match.list]). If a narrowing conversion (see below) is required
to convert any of the arguments, the program is ill-formed.
(3.7) Otherwise, if T is an enumeration with a fixed underlying type
([dcl.enum]), the initializer-list has a single element v, and the
initialization is direct-list-initialization, the object is
initialized with the value T(v) ([expr.type.conv]); if a narrowing
conversion is required to convert v to the underlying type of T,
the program is ill-formed.
(3.8) Otherwise, if the initializer list has a single element of type E
and either T is not a reference type or its referenced type is
reference-related to E, the object or reference is initialized from
that element (by copy-initialization for copy-list-initialization, or
by direct-initialization for direct-list-initialization); if a
narrowing conversion (see below) is required to convert the element to
T, the program is ill-formed.
(3.9) Otherwise, if T is a reference type, a prvalue of the type referenced by T is generated. The prvalue initializes its result
object by copy-list-initialization or direct-list-initialization,
depending on the kind of initialization for the reference. The prvalue
is then used to direct-initialize the reference.
(3.10) Otherwise, if the initializer list has no elements, the object is value-initialized.
(3.11) Otherwise, the program is ill-formed.
(Note: (3.6) is not possible in this case, for the following reason: (3.4) covers the case where a default constructor is present. In order for (3.6) to be considered, a non-default constructor has to be called, which is not possible with an empty initializer list. (3.11) is not possible because (3.10) covers all cases.)
Now let's analyze the cases:
(3.3)
For an aggregate, value-initialization first performs zero-initialization and then, if the element has a non-trivial default constructor, default-initialization, on the aggregate, per [dcl.init]/8:
To value-initialize an object of type T means:
[...]
if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is
zero-initialized and the semantic constraints for
default-initialization are checked, and if T has a non-trivial default
constructor, the object is default-initialized;
[...]
Non-union aggregates
When copy-initializing a non-union aggregate from {}, elements that are not explicitly initialized with a default member initializer are copy-initialized from {} per [dcl.init.aggr]/8:
If there are fewer initializer-clauses in the list than there are
elements in a non-union aggregate, then each element not explicitly
initialized is initialized as follows:
If the element has a default member initializer ([class.mem]), the element is initialized from that initializer.
Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
Otherwise, the program is ill-formed.
[...]
See Case 1.
Union aggregates
If the aggregate is a union, and no member has a default member initializer, then copying-initializing the aggregate from {} copy-initializes the first element from {}: [dcl.init.aggr]/8:
[...]
If the aggregate is a union and the initializer list is empty, then
if any variant member has a default member initializer, that member is initialized from its default member initializer;
otherwise, the first member of the union (if any) is copy-initialized from an empty initializer list.
See Case 1, variant.
(3.4)
Value-initialized, so no difference.
(3.9)
T() isn't allowed if T is a reference per [dcl.init]/9:
A program that calls for default-initialization or
value-initialization of an entity of reference type is ill-formed.
See Case 2.
(3.10)
Similarly, value-initialized. No difference.
is there any different between empty parentheses and empty braces when used as initializers
There are cases where empty parentheses cannot be used as initialiser because it would be syntactically ambiguous with a function declaration:
T t(); // function declaration; not initialisation
T t{}; // value initialisation
More formally, given a type T, is it possible that T() and T{} are different?
The ambiguity described above has a case where T() is parsed as pointer to function, known as the Most Vexing Parse:
U t(T()); // function declaration; not initialisation
U t(T{}); // value initialisation, and direct initialisation

Brace initialization of numeric types. Are they 0-initialized?

I would like to be sure that the following
int i{};
double x{};
initialize all the variables to 0. My compiler seems to do that in all modes, but I need to be sure that it is clearly stated by the standard.
Any reference to the C++11 standard is welcome.
This is stated by the standard (all quotes from N3337).
T x{}; is list-initialization.
[dcl.init.list]/1: List-initialization is initialization of an object or reference from a braced-init-list.Such an initializer is
called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the
initializer list. An initializer list may be empty. [...]
The applicable definition for list-initialization:
[dcl.init.list]/3: List-initialization of an object or reference of type T is defined as follows:
[lots of non-applicable rules]
Otherwise, if the initializer list has no elements, the object is value-initialized.
So that form for built-in types is value-initialization:
[dcl.init]/7: To value-initialize an object of type T means:
[non-applicable rules]
otherwise, the object is zero-initialized.
So now we're looking for zero-initialization (yes, C++ has a lot of types of initialization):
[dcl.init]/5: To zero-initialize an object or reference of type T means:
if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;
[...]
Yay, since arithmetic types are scalar types ([basic.types]/9 if you don't trust me), these forms both initialize their objects with 0.
Yes, this is guaranteed by the standard: this is actually performing value-initialization.
In particular, see the point 4) on the page: it states that it has to be value-initialization:
Value initialization is performed in these situations:
...
4) when a named variable (automatic, static, or thread-local) is declared with the initializer consisting of a pair of braces.
And on the same page, you see that the effect of value-initialization for built-in types is to initialize them with 0 (square braces are mine):
The effects of value initialization are:
...
4) Otherwise [if non-class, non-array type], the object is zero-initialized.
The int i{}; form is called value initialization.
Abridged:
The effects of value initialization are:
[...]
4) Otherwise [if T is not a class or array type], the object is zero-initialized.

Meaning of default initialization changed in C++11?

C++2003 8.5/5 says:
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);
— if T is an array type, each element is default-initialized;
— otherwise, the object is zero-initialized.
[Emphasis added.]
The C++2011 standard changed that last item to
— otherwise, no initialization is performed.
This seems like it would be a breaking change for some programs. Was this intentional?
Edit
Here's some code to motivate this question:
class Foo {
public:
Foo() : m_values() {}
int m_values[3];
};
Before C++11, I thought the explicit mention of m_values in the default constructor would default-initialize that array. And since the elements of the array are scalar, I expected that to mean the values were all set to 0.
In C++11, it seems there's no longer a guarantee that this will happen. But maybe, as Mooing Duck pointed out in the comments, perhaps this is no longer a case of default initialization but some other form which preserves the expected behavior. Citations welcome.
The final effects are almost the same. In C++03, the use of default-initialize was restricted to non-POD class type, so the last point never applied. In C++11, the standard simplifies the wording by eliminating the condition with regards to where default-initialization was used, and changes the definition of default-initialization to cover all of the cases in a way to correspond what happened before.
According to cppreference.com (because it uses friendlier language than the standard):
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.
Value initialization is performed in three situations:
3,7) when a non-static data member or a base class is initialized
using a member initializer with an empty pair of parentheses or braces (since C++11)
Note that the C++11 part belongs with the or braces, not with the entire paragraph.
And:
To value-initialize an object of type T means:
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized
So in C++11 default-initialization does not zero-initialize members but value-initialization does.
Strictly speaking, the definition of default-initialize has changed from C++03 to C++11. But one has also to take into account that the situations when an object is _default-initialize_d changed:
§8.5p9 C++03 states:
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for a nonstatic object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
§8.5p11 C++11 states:
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.
As #JamesKanze already pointed out, default-initialization is performed in C++03 when no initializer for an object of non-POD class type is specified. In C++11 an object (of arbitrary type) is default-initialized if no initializer is specified. Because of this change, the definition of default-initialize had also to be changed in order to be compatible with C++03.
Your example has nothing to do with default-initialization. It has been always the case that an object whose initializer is an empty set of parentheses is value-initialized.

Default values in C++ initializer lists

I only just learned yesterday that specifying parameters to initializer list items is optional. However, what are the rules for what happens in this case?
In the below example, will ptr be initialized to 0, toggle to false, and Bar default-constructed? I guess this question is sort of redundant, because there would be little point in initializer lists if unspecified argument values == undefined behavior.
Could I also be pointed to the section of the C++ standard that states the behavior in the case of initializer list items not being given arguments?
class Bar
{
Bar() { }
};
class SomeClass;
class AnotherClass
{
public:
SomeClass *ptr;
bool toggle;
Bar bar;
AnotherClass() : ptr(), toggle(), bar() { }
// as opposed to...
// AnotherClass() : ptr(NULL), toggle(false), bar(Bar()) { }
};
Yes, the members will be initialized to zero and a default-constructed object respectively.
The C++ 11 standard specifies this behavior in 12.6.2/7:
The expression-list or braced-init-list in a mem-initializer is used
to initialize the designated subobject (or, in the case of a
delegating constructor, the complete class object) according to the
initialization rules of 8.5 for direct-initialization.
In turn, 8.5/10 reads:
An object whose initializer is an empty set of parentheses, i.e., (),
shall be value-initialized.
Paragraph 8.5/7 defines value-initialized:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor
(12.1), then the default constructor for T is called (and the
initialization is ill-formed if T has no accessible default
constructor);
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.
if T is an array type,
then each element is value-initialized;
otherwise, the object is
zero-initialized.
And finally, 8.5/5 defines zero-initialized:
To zero-initialize an object or reference of type T means:
if T is a
scalar type (3.9), the object is set to the value 0 (zero), taken as
an integral constant expression, converted to T;
if T is a
(possibly cv-qualified) non-union class type, each non-static data
member and each base-class subobject is zero-initialized and padding
is initialized to zero bits;
if T is a (possibly cv-qualified) union
type, the object’s first non-static named data member is zero-
initialized and padding is initialized to zero bits;
if T is an
array type, each element is zero-initialized;
if T is a reference
type, no initialization is performed.
In the below example, will ptr be initialized to 0, toggle to false, and Bar default-constructed?
Yes. If a member initialiser appears in the initialiser list with empty parentheses, then that member is value initialised. This means that numerical types will be initialised to zero, pointers to null, and classes with default constructors using that constructor.
If you don't include the member in the initialiser list at all, then it will instead be default initialised; in that case. numerical and pointer types will be left uninitialised.
Could I also be pointed to the section of the C++ standard that states the behavior in the case of initializer list items not being given arguments?
C++11 12.6.2/7 specifies that the rules are the same as for direct initialisation.
C++11 8.5/16 specifies that if the initialiser is (), the object is value-initialised.
C++11 8.5/7 defines value initialisation.
Initialisations are covered in [dcl.init] (aka 8.5)
Point 10 says:
An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
Value-initialisation is, put simply, default construction for classes and zero-initialisation for non-class types.

3 types of Initializations [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
What do the following phrases mean in C++: zero-, default- and value-initialization?
Today I came to know about 3 types of initialization in C++:
Zero Initialization
Default Initialization
Value Initialization
I have googled about it but I found no satisfactory results. All I get is a few standards. What I have understood until now is this: in case of value initialization, a data member can get value equal to zero in some cases.
Please elaborate them (standards) with examples. Also please don't just provide the text from the standard.
Thanks
The types of initialization refer to the language grammar. Here are two examples:
T * p1 = new T;
T * p2 = new T();
The object *p1 is default-initialized, and the object *p2 is value-initialized.
The effect of the initialization depends on the type T: 1) If T is a fundamental, then default-initialization does nothing (i.e. the object is left uninitialized), while value initialization equals zero initialization in that case and means the object is set to zero.
2) If T is an aggregate (i.e. class without constructors or destructor or assignment operator), then each element is recursively default- or value-initialized.
3) If T is of class-type and does have user-defined constructors, then both default- and value-initialization cause a call to the default constructor.
Note that member objects of classes with constructors can in turn be default- or value-initialized:
struct Foo {
int x;
int y;
Foo() : x() { }
};
Now when you say Foo a; then a is default-initialized, so the default constructor is called. This in turn causes a.x to be value-, i.e. zero-initialized, while a.y remains default-, i.e. un-initialized.
(Note that it's not really possible to value-initialize an automatic object, though in C++11, brace-initialization may be used to cause value-initialization, as in Foo a{};. (This behaves exactly the same as Foo a; in our example, consequent to the third paragraph.))
This is dealt with in 8.5 Initializers [dcl.init].
Zero Initialization
5/ To zero-initialize an object or reference of type T means:
— if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T. As specified in 4.10, converting an integral constant expression whose value is 0 to a pointer type results in a null pointer
value.
— if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
— if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zeroinitialized and padding is initialized to zero bits;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.
Basically, it's equivalent to a memset(&obj, 0, sizeof(objt));, except that it account that the memory representation of a null pointer might not be a 0 value (even though it is represented by a 0 in the language).
// foo.cpp
static char const* p; // p is zero-initialized
// during static initialization
static void init() {
if (!p) { p = new char[500]; } // fine as p has been 0-initialized
}
Note: personally I still prefer to use = nullptr to initialize p, just to make the intent clear...
Default Initialization
6/ To default-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, no initialization is performed.
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
Or basically, a call to the default constructor, accounting for arrays, at least for classes. The last point is a caveat for built-ins (such as int). Those are simply left as is (with garbage inside).
Default initialization is what is called when you defined a variable but do not initialize it explicitly. It is also what happens to attributes of a class that are not listed in the initializer list. So the caveat for built-ins is quite important to a programmer.
int function() {
int a; // <-- a is default-initialized (which means nothing happens...)
return a; // <-- uses a, so technically undefined behavior
}
struct A { int a; A() {} }; // During the call to A::A(),
// A::a is default-initialized (nothing happens...)
The absence of explicit initialization is a left-over from C. It's normally so for optimization reasons but leads to Undefined Behavior if one attempts to use the value...
Value Initialization
7/ To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— 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.
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized.
An object that is value-initialized is deemed to be constructed and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed,” etc., even if no constructor is invoked for the object’s initialization.
It is a mix of both the above, meaning that the following syntax:
template <typename T> T value() { return T(); }
^~~
provides a suitably initialized instance of T whether T is a class type or a built-in type. It's important for templated code to be able to have such a unified syntax.
Note that with C++11, it is also possible to use T{} to achieve the same effect (which helps disambiguates from functions).