Is `T array[N]{}` a value initialization or aggregate initialization? - c++

Consider some type T (for simplicity, you may assume int) and some integral constant N, which we use to define an array like this:
T array[N]{}; // Note the empty braces here!
According to cppreference, value initialization is defined as follows:
This is the initialization performed when an object is constructed with an empty initializer.
But further down it is written:
In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.
But then a little bit more down, the following statement appears:
if T is an array type, each element of the array is value-initialized;
From my understanding, the first and third quoted statements contradict to the second one.
So my two questions are:
Is the code snippet above a value initialization or an aggregate initialization?
Do the three quoted statements really contradict or am I missing something?
Note: I've seen similar questions here but they all differ a bit in the specifics.

Is T array[N]{} a value initialization or aggregate initialization?
It is list initialization and part of this initialization process involves aggregate initialization as per dcl.init.list. Additionally, it is also direct list initialization as quoted below.
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 initializer-list or designated-initializer-clauses of the designated-initializer-list are called the elements of the initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.
[Note 1 : List-initialization can be used
(1.1) as the initializer in a variable definition ([dcl.init])
...
— end note]
The above means that T array[N]{} is list-initialization.
Now let's move on to how the elements of the array is initialized which is given in dcl.init.list#3:
3) List-initialization of an object or reference of type T is defined as follows:
3.4) Otherwise, if T is an aggregate, aggregate initialization is performed.
And since in our example T array[N] is an aggregate, the above implies that in our example the whole process of initialization of the array T array[N] involves aggregate initialization.
Finally, from aggregate initialization given below, we will note that each element is copy-initialized from an empty initializer list:
3) When an aggregate is initialized by an initializer list as specified in [dcl.init.list], the elements of the initializer list are taken as initializers for the elements of the aggregate. The explicitly initialized elements of the aggregate are determined as follows:
3.3) Otherwise, the initializer list must be {}, and there are no explicitly initialized elements.
The above means that there are no explicitly initialized elements in our example so we move onto dcl.init.aggr#5:
5) For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:
5.2) Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list.
(emphasis mine)
Essentially, this means that each element of type T of the array will be initialized from an empty initializer list.
Note that this also explains why the following contrived example fails in C++20:
struct T
{
T() = delete;
};
int main()
{
T array[5]{}; //this fails as a consequence of above explanation
}

Related

Is this a list initialization or a value initialization?

int i {};
Is this List Initialization or Value Initialization?
I can't distinguish them because I can't understand this sentence: a possibly empty brace-enclosed list of expressions or nested braced-init-lists from the link: https://en.cppreference.com/w/cpp/language/list_initialization
Is this List Initialization or Value Initialization?
It is direct-list-initialization
T object { arg1, arg2, ... }; (1)
direct-list-initialization (both explicit and non-explicit constructors are considered)
initialization of a named variable with a braced-init-list (that is, a possibly empty brace-enclosed list of expressions or nested braced-init-lists)
and the effect on T is value-initialization
Explanation
The effects of list initialization of an object of type T are:
[...]
Otherwise, if the braced-init-list has no elements, T is value-initialized.
And now for the value-initialization of integer you get zero-initialized.
Explanation
Value initialization is performed in these situations:
[...]
otherwise, the object is zero-initialized.
Whether it is a List or Value initialization depends on the object you are initializing. See https://en.cppreference.com/w/cpp/language/value_initialization:
If T is a class type that has no default constructor but has a constructor taking std::initializer_list, list-initialization is performed.
So since the object in this case in int which does not have a constructor taking a std::initializer_list and int is not an aggregate type, this is value initialization.

C++11 class member initialization

Just switched to C++11 from C++03, and I was wondering, is the following defined to always zero initialize the array data for all elements?
template<size_t COUNT>
class Test {
public:
uint32 data[COUNT] = {};
};
Yes it's guaranteed; list initialization turns to aggregate initialization for array type:
Otherwise, if T is an aggregate type, aggregate initialization is performed.
then for aggregate initialization:
If the number of initializer clauses is less than the number of members or initializer list is completely empty, the remaining members are initialized by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates).
So all the elements of data will be value initialized, for uint32 they'll be zero-initialized at last.
otherwise, the object is zero-initialized.

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.

Zero-Initialize array member in initialization list

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.

Zero-initialization of POD types

struct Foo
{
char name[10];
int i;
double d;
};
I know that I can zero-initialize all the members of such POD type with:
Foo foo = {0};
Can I further simplify this to:
Foo foo = {};
Like native arrays? (int arr[10] = {};)
I'm not asking when initializing with {0}, will the members except the first are zero-initialized. I know the answer to that question is yes. I'm asking if the first 0 can be omitted syntactically.
Most of the tutorials I found on this subject suggest using {0}, none using {}, e.g, this guide, and it's explained as This works because aggregate initialization rules are recursive;, which gives more confusion than explanation.
As written, this is aggregate initialization. The applicable rule is (§8.5.1 [dcl.init.aggr]/p7):
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).
The relevant parts of §8.5.4 [dcl.init.list]/p3 is:
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).
Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is
value-initialized.
[irrelevant items omitted]
Otherwise, if the initializer list has no elements, the object is value-initialized.
In short, sub-aggregates are recursively aggregate-initialized from an empty initializer list. Everything else is value-initialized. So the end result is everything being value-initialized, with everything being a POD, value-initialization means zero-initialization.
If T is POD but not an aggregate, then aggregate initialization doesn't apply, so you hit the second bullet point in §8.5.4 [dcl.init.list]/p3, which results in value-initialization of the entire object instead. POD classes must have a trivial (and so not-user-provided) default constructor, so value-initialization for them means zero-initialization as well.