List initialization - What changed in C++14? - c++

These two lines from cppreference
What is the difference between these two statements ? I don't see any difference
until c++14
If the braced-init-list is empty and T is a class type with a default
constructor, value-initialization is performed. Otherwise, if T is an
aggregate type, aggregate initialization is performed.
since c++14
If T is an aggregate type, aggregate 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.

The difference is which one happens when both conditions apply: if T is an aggregate class (as opposed to an array), which certainly has a default constructor, and the braced-init-list is empty. Of course, to understand why that matters, we then have to distinguish value initialization from aggregate initialization from an empty list.
Value initialization zero-initializes the object and then default-initializes it, which for an aggregate is default-initializing each of its members, so the value-initialization is member-wise (plus zeroing padding). Aggregate initialization initializes each member from {}, which is again value initialization for many types but is default initialization for members of class type with a user-provided default constructor. The difference can be seen in
struct A {A() {} int i;};
struct B {A a;}; // aggregate
B b{}; // i is 0 in C++11, uninitialized in C++14
B b2=B(); // i is 0 in both versions
In C++14 only, aggregates can have default member initializers; that can't contribute to a difference in behavior between the two language versions, of course, but it doesn't behave differently between these two rules anyway (since it replaces only the common default initialization).

The difference is the sequence of checking, so the aggregate type checking is taking place at the first place, and only then the rest.

Related

Aggregate initialization vs uniform initialization

The more I read about C++11 uniform initialization, the more confused I am.
Scott Meyers in Effective Modern C++ (page 55) says that the statement
Widget w2{};
always calls the default constructor (even in the presence of constructor with a std::initializer_list argument).
At first sight, this seems to be consistent with the 4th edition of Stroustrup book "C++ programing language", e.g. according to the table on page 1200 the statement
std::atomic<T> x;
leaves the atomic variable uninitialized, while
std::atomic<T> x {};
calls "default constructor" so that x represents a value initialized T object.
However, I can't believe that std::atomic<T> x; does not call the default constructor in C++11 anymore, so I'm totally confused here.
Finally, after taking a look at C++11 standard (n3337 draft) my confusion is even bigger.
On page 1102 we have:
template <> struct atomic<integral > {
//[...] list of non-constructor functions
atomic() noexcept = default;
constexpr atomic(integral ) noexcept;
atomic(const atomic&) = delete;
//[...] other non-constructor functions
};
While on page 1104 (point 29.5.5) we see
The atomic integral specializations and the specialization
atomic shall have standard layout. They shall each have a
trivial default constructor and a trivial destructor. They shall each
support aggregate initialization syntax.
So classes with user defined constructor now support aggregate initialization?
Is this so because the constuctor is constexpr?
And what happens when we write
std::atomic<T> x {};
Is this the aggregate initialization? Or a call to the (trivial) default constructor?
So classes with user defined constructor now support aggregate initialization?
Only aggregates support aggregate initialization. An aggregate can have a user defined constructor only if the constructor is defined as defaulted or deleted.
This will change in C++20 where no user declared constructors are allowed at all.
Is this so because the constuctor is consexpr?
constexpr has no effect on this.
std::atomic<T> x {};
Is this the aggregate initialization?
This is list initialization. If the type is an aggregate, then list initialization would aggregate initialize the object. std::atomic is not an aggregate, because it has a user-provided constructor that is neither defaulted nor deleted:
constexpr atomic( T desired ) noexcept; // (2) (since C++11)
For std::atomic, this rule of list initialization applies:
Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
And value initialization invokes the default constructor for such class.

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.

Default argument using curly braces initializer

I have this snippet of code which seems to work well:
class foo{/* some member variables and functions*/};
void do_somthing(foo x={}){}
int main(){
do_somthing();
}
I used to use void do_somthing(foo x=foo()){} to default the x argument but I see this way ={} in some book or online example(can not remember). Is it totally ok to use it? Is there any difference between the two methods?
foo x=foo() is copy initialization,
Initializes an object from another object
and foo() is value initialization.
This is the initialization performed when a variable is constructed with an empty initializer.
foo x={} is aggregate initialization.
Initializes an aggregate from braced-init-list
If the number of initializer clauses is less than the number of
members and bases (since C++17) or initializer list is completely
empty, the remaining members and bases (since C++17) are initialized
by their default initializers, if provided in the class definition,
and otherwise (since C++14) by empty lists, which performs
value-initialization.
So the result is the same in this case (both value-initialized).
And the effects of value initialization in this case are:
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
Finally the effects of zero initialization in this case are:
If T is a scalar type, the object's initial value is the integral
constant zero explicitly converted to T.
If T is an non-union class type, all base classes and non-static data
members are zero-initialized, and all padding is initialized to zero
bits. The constructors, if any, are ignored.

Is this aggregate initialization or default initialization in C++?

Consider following program.
#include <iostream>
int main()
{
int a=int{};
std::cout<<a;
}
Is it uses aggregate initialization or default initialization? I am confused.
Empty parentheses or braces (T() or T{}) perform value initialization. The exception would be the case where the type is an aggregate in which case aggregate initialization would be used. Since int is not an aggregate, it will be value initialized and since it's not a class nor an array, value initialization will do zero-initialization.
You were wondering why it doesn't work in C. Such syntax simply doesn't exist in C, see this answer.
Aggregate initialization is a type of list initialization, which initializes aggregates. An aggregate is an object of type array, or object which has the characteristics defined on this page.
In this case, the type of initialization is most likely value initialization.
Since C++11, by comparison with other SO answers (e.g.: this or this), I would say this is:
a value-initialization (int{}) followed by
a copy-initialization (int a=int{}).
By the way, from C++17, the second step should vanish as int{} is required to directly initialize a.

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.