Ideone is correct as far as C++14 (N3797) is concerned (see Casey's answer for C++11) because a[1]
and a[2]
are initialized with A{}
, which is value initialization, causing i
to be 0. This comes from N3797 § 8.5.1/7:
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). [ Example:
struct S { int a; const char* b; int c; int d = b[a]; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", ss.c with the value of an expression of the form int{} (that is, 0), and ss.d with the value of ss.b[ss.a] (that is, ’s’)
An array is an aggregate per § 8.5.1/1 (An aggregate is an array...
), so this applies to the array's initialization.
The expression T{}
(an empty initializer list) value-initializes the object per § 8.5.4 /3:
Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
We can confirm that the value-initialization leaves i
with a value of 0 with § 8.5/8:
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;
Your class has no user-provided default constructor per § 8.4.2/4:
A function is user-provided if it is user-declared and not explicitly defaulted or
deleted on its first declaration.
An interesting point to note is that if your default constructor was user-provided and did not initialize i
, the first point of 8.5/8 would be used and i
would be left uninitialized, as you can see in this example.
Finally, a small note on the comparison. Ideone uses a few different versions of GCC. Which one is used makes a difference (you can check with __VERSION__
if you need to). The compiler flags also make a slight difference in this case. If -std=c++1y
is present (and I don't know a way to check that other than using an added feature), there will be some C++14 support, but not complete support, so the small changes (T{}
vs. T()
, initializing from a brace-or-equal-initializer and checking the semantic constraints for default-initialization) might not be implemented. In fact, you can even check the first. Coliru lets you configure the build command, so just saying Coliru is extremely ambiguous.
Either way, using N3797 to test for conforming behaviour isn't too worthwhile until enough C++14 support comes along (or at least until it's standardized). I'm tending to stick with N3485 until that happens. In this specific example, I don't think there is any difference between the two standards' behaviours. Check Casey's answer for how the two standards differ in this matter. You have a conversion constructor, so your objects would be default-initialized in C++11.
A
s in the array to value initialize the correspondingi
s. – juanchopanzaint a[3] = {1};
leavinga[1]
anda[2]
as 0, but with a class instead, and for some reason, that's the one rule you get taught in beginner classes that I always forget at first. – chris