Is the following syntax:
T t = {};
a zero-initalization or a value-initalization that for non-POD types is zero-initialization?
The syntax T object = {arg1, arg2, ...}; is copy list initialization. Since you established that T is not an aggregate type and that T has a default constructor (from the list) then list initialization will perform value initialization :
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.
Related
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.
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.
This code compiles fine with GCC 5.X, MSVC, but GCC 6.X gives error:
"converting to 'a' from initializer list would use explicit
constructor 'a::a()'", clang "chosen constructor is explicit in
copy-initialization".
Removing explicit or changing to a c{} fixes the problem, but I`m curious why it works this way.
class a
{
public:
explicit a () {}
};
struct b
{
a c;
};
int main() {
b d{};
}
b is an aggregate. When you initialize it using an initializer list, the elements in the list will initialize the first n members of the aggregate, where n is the number of elements in the list. The remaining elements of the aggregate are copy-list-initialized.
So in your example, c will be copy-list-initialized, but that is ill-formed if the chosen constructor is explicit, hence the error.
The relevant standard quotes are
[dcl.init.aggr]/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:
...
— If the initializer list is an initializer-list, the explicitly initialized elements of the aggregate are the first n elements of the aggregate, where n is the number of elements in the initializer list.
— Otherwise, the initializer list must be {}, and there are no explicitly initialized elements.
[dcl.init.aggr]/5
For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:
...
— Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
The effect of copy initializing c from an empty initializer list is described in
[dcl.init.list]/3
List-initialization of an object or reference of type T is defined as follows:
...
— Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
[dcl.init]/8
To value-initialize an object of type T means:
...
— if T is a (possibly cv-qualified) class type with either no default constructor ([class.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;
[dcl.init]/7
To default-initialize an object of type T means:
— If T is a (possibly cv-qualified) class type, constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one for the initializer () is chosen through overload resolution. The constructor thus selected is called, with an empty argument list, to initialize the object.
[over.match.ctor]
... For copy-initialization, the candidate functions are all the converting constructors of that class.
[class.conv.ctor]/1
A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters (if any) to the type of its class. Such a constructor is called a converting constructor.
In the example above, a has no converting constructors, so overload resolution fails. The (non-normative) example in [class.conv.ctor]/2 even contains a very similar case
struct Z {
explicit Z();
explicit Z(int);
explicit Z(int, int);
};
Z c = {}; // error: copy-list-initialization
You can avoid the error by providing a default member initializer for c
struct b
{
a c{}; // direct-list-initialization, explicit ctor is OK
};
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.
If I declare a class with default constructor and define a instance of this class with initializer list as below, will the default constructor be called for this definition? And why been called or not been called?
class Sample
{
// this should be any aggregate type in c++
};
int main()
{
Sample s = {0};
return 0;
}
In C++03, only aggregate classes may be initialized with curly braces, and an aggregate class may not have a user defined constructor
In C++0x, this syntax is supported for non aggregate types through initializer lists and calls the appropriate constructor (taking a std::initializer_list)
When you provide an brace enclosed initializer all the members of the class are copy-initialized from the corresponding expression of the brace enclosed initializer.
Such initialization is only valid for aggregates which cannot have user-declared constructors so the suppression of the compiler generated constructor is almost academic.
The Standard says ($8.5/14)
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. The source type is not defined when the initializer is brace-enclosed or when it is a parenthesized list of expressions.
If the destination type is a (possibly cv-qualified) class type:
— If the class is an aggregate (8.5.1), and the initializer is a brace-enclosed list, see 8.5.1.
.
.
8.5.1/13
[Note: An aggregate array or an aggregate class may contain members of a class type with a user-declared constructor (12.1). Initialization of these aggregate objects is described in 12.6.1. ]
Also 12.6.1/2 says
When an aggregate (whether class or array) contains members of class type and is initialized by a brace-enclosed initializer-list (8.5.1), each such member is copy-initialized (see 8.5) by the corresponding assignment-expression. If there are fewer initializers in the initializer-list than members of the aggregate,
each member not explicitly initialized shall be value-initialized (8.5).
The corresponding constructor is called:
http://en.wikipedia.org/wiki/C%2B%2B0x#Initializer_lists
In C++ you may only initialize POD (plain old data) with ={0} (at least pre C++0x). So the default constructor will not be called because this won't compile.