initializer lists in C and C++ - c++

For some reason I had this idea that C and C++ worked like this:
int foo[10] = {57};
for (int i=0; i<10; ++i)
assert (foo[i] == 57);
Turns out the remaining ints are initialized to 0, not 57. Where did I get this idea? Was this true at one point? Was it ever true for structure initializer lists? When did arrays and structures neatly and correctly start initializing themselves to 0 values when assigned to = {} and = {0}? I always thought they'd initialize to garbage unless explicitly told otherwise.

It's been this way forever, as long as initializers existed. C89 says:
If there are fewer initializers in a list than there are members of an
aggregate, the remainder of the aggregate shall be initialized
implicitly the same as objects that have static storage duration.

Where did I get this idea?
It's apparently a relatively common misconception, as I've heard the same thing from a number of other people recently. Perhaps you picked it up from somebody else with this wrong idea, or perhaps the idea is just 'intuitive'.
{} Initialization has worked as it does at least as far back as C89. I'm not aware of it ever working differently, or any compilers that ever did it differently.
For initializer lists when initializing an aggregate type (like an array):
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 an empty initializer list (8.5.4). — Aggregates [dcl.init.aggr] 8.5.1p7
In C++ terms, when you use an empty initializer the object is 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.
— Initializers [dcl.init] 8.5p7

Related

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.

Does a default constructor always initialize all members?

I could swear I don't remember having seen this before, and I'm having trouble believing my eyes:
Does an implicitly-defined default constructor for a non-aggregate class initialize its members or no?
In Visual C++, when I run this innocent-looking code...
#include <string>
struct S { int a; std::string b; };
int main() { return S().a; }
... to my astonishment, it returns a non-zero value! But if I remove field b, then it returns zero.
I've tried this on all versions of VC++ I can get my hands on, and it seems to do this on all of them.
But when I try it on Clang and GCC, the values are initialized to zero, whether I try it in C++98 mode or C++11 mode.
What's the correct behavior? Is it not guaranteed to be zero?
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.
(All quotes in the first section are from N3337, C++11 FD with editorial changes)
I cannot reproduce the behavior with the VC++ on rextester. Presumably the bug (see below) is already fixed in the version they are using, but not in yours - #Drop reports that the latest release, VS 2013 Update 4, fails the assertion - while the VS 2015 preview passes them.
Just to avoid misunderstandings: S is indeed an aggregate. [dcl.init.aggr]/1:
An aggregate is an array or a class (Clause 9) with no user-provided
constructors (12.1), no private or protected non-static data members
(Clause 11), no base classes (Clause 10), and no virtual functions
(10.3).
That is irrelevant though.
The semantics of value initialization are important. [dcl.init]/11:
An object whose initializer is an empty set of parentheses, i.e.,
(), shall be value-initialized.
[dcl.init]/8:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a
default constructor that is user-provided or deleted, then the object is default-initialized;
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;
[..]
Clearly this holds regardless of whether b is in S or not. So at least in C++11 in both cases a should be zero. Clang and GCC show the correct behavior.
And now let's have a look at the C++03 FD:
To value-initialize an object of type T means:
if T is a class type (clause 9) with a user-declared constructor (12.1) [..]
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;
if T is an array type, then each element is value-initialized;
otherwise, the object is zero-initialized
That is, even in C++03 (where the above quote in [dcl.init]/11 also exists in /7), a should be 0 in both cases.
Again, both GCC and Clang are correct with -std=c++03.
As shown in hvd's answer, your version is compliant for C++98, and C++98 only.

Value initialization: default initialization or zero initialization?

I have templated gray_code class which is meant to store some unsigned integer whose underlying bits are stored in Gray code order. Here it is:
template<typename UnsignedInt>
struct gray_code
{
static_assert(std::is_unsigned<UnsignedInt>::value,
"gray code only supports built-in unsigned integers");
// Variable containing the gray code
UnsignedInt value;
// Default constructor
constexpr gray_code()
= default;
// Construction from UnsignedInt
constexpr explicit gray_code(UnsignedInt value):
value( (value >> 1) ^ value )
{}
// Other methods...
};
In some generic algorithm, I wrote something like this:
template<typename UnsignedInt>
void foo( /* ... */ )
{
gray_code<UnsignedInt> bar{};
// Other stuff...
}
In this piece of code, I expected bar to be zero-intialized and therefore bar.value to be zero-initialized. However, after having struggled with unexpected bugs, it appears that bar.value is initialized with garbage (4606858 to be exact) instead of 0u. That surprised me, so I went to cppreference.com to see what the line above was exactly supposed to do...
From what I can read, the form T object{}; corresponds to value initialization. I found this quote interesting:
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.
However, gray_code has a user-provided constructor. Therefore it is not an aggregate thus aggregate initialization is not performed. gray_code has no constructor taking an std::initializer_list so list initialization is not performed either. The value-initialized of gray_code should then follow the usual C++14 rules of value initialization:
1) If T is a class type with no default constructor or with a user-provided default constructor or with a deleted default constructor, the object is default-initialized.
2) If T is a class type without a user-provided or deleted default constructor (that is, it may be a class with a defaulted default constructor or with an implicitly-defined one) then 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.
If I read correctly, gray_code has an explicitly defaulted (not user-provided) default constructor, therefore 1) does not apply. It has a defaulted default constructor, so 2) applies: gray_code is zero-initialized. The defaulted default constructor seems to meet all the requirements of a trivial default constructor, so default initialization should not happen. Let's have a look then at how gray_code is zero-initialized:
If T is a scalar type, the object's initial value is the integral constant zero implicitly 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.
If T is a union type, the first non-static named data member is zero-initialized and all padding is initialized to zero bits.
If T is array type, each element is zero-initialized
If T is reference type, nothing is done.
gray_code is a non-union class type. Therefore, all of its non-static data members should be initialized which means that value is zero-initialized. value satisfies std::is_unsigned and is therefore a scalar type, which means that it should be initialized with "the integral constant zero implicitly converted to T".
So, if I read correctly all of that, in the function foo above, bar.value should always be initialized with 0 and it should never be initialized with garbage, am I right?
Note: the compiler I compiled my code with is MinGW_w4 GCC 4.9.1 with (POSIX threads and dwarf exceptions) in case that helps. While I sometimes get garbage on my computer, I never managed to get anything else than zero with online compilers.
Update: It seems to be a GCC bug that the error is mine and not that of my compiler. Actually, when writing this question, I assumed for the sake of simplicity that
class foo {
foo() = default;
};
and
class foo {
foo();
};
foo::foo() = default;
were equivalent. They are not. Here is the quote from the C++14 standard, section [dcl.fct.def.default]:
A function is user-provided if it is user-declared and not explicitly defaulted or
deleted on its first declaration.
In other words, when I got garbage values, my defaulted default constructor was indeed user-provided since it was not explicitly efaulted on its first declaration. Therefore, what happened was not zero initialization but default initialization. Thanks #Columbo again for pointing out the real problem.
So, if I read correctly all of that, in the function foo above,
bar.value should always be initialized with 0 and it should never be
initialized with garbage, am I right?
Yes. Your object is direct-list-initialized. C++14's* [dcl.init.list]/3 specifies that
List-initialization of an object or reference of type T is defined
as follows:
[… Inapplicable bullet points…]
Otherwise, 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.
[…]
Your class isn't an aggregate since it has user-provided constructors, but it does have a default constructor. [dcl.init]/7:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that
is user-provided or deleted, then the object is default-initialized;
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;
[dcl.fct.def.default]/4:
A special member function is user-provided if it is user-declared and
not explicitly defaulted […] on its first declaration.
So your constructor is not user-provided, therefore the object is zero-initialized. (The constructor is not called since its trivial)
And finally, in case this was not clear, to zero-initialize an object or reference of type T means:
if T is a scalar type (3.9), the object is initialized to the value obtained by converting the integer literal 0 (zero) 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;
[…]
Thus either
Your compiler is bugged
…or your code triggers undefined behavior at some other point.
* The answer is still yes in C++11, though the quoted sections are not equivalent.

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.

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.