OK, this is pretty simple to trace. The presence of {}
means that list initialization is being performed, so we get to visit our favorite part of the spec: [dcl.init.list]/3.
The object being initialized in case 1 is a char[4]
. The braced-init-list is not a designated initializer, so 3.1 is ignored. char[4]
is not a class, so 3.2 is ignored. That brings us to 3.3:
Otherwise, if T
is a character array and the initializer list has a single element that is an appropriately-typed string-literal ([dcl.init.string]), initialization is performed as described in that subclause.
Well, char[4]
is definitely a character array, and the initializer list definitely contains a single element, and that element does in fact match the type of the character array. So off to [dcl.init.string] we go.
That tells us (after a fashion):
Successive characters of the value of the string-literal initialize the elements of the array.
But the next paragraph warns:
There shall not be more initializers than there are array elements.
Well, that makes #1 ill-formed.
So, we redo the process for char[5]
. And that doesn't trigger, since 5 is sufficiently large.
Lastly, we come to char[]
. Which is no different from using a number, as far as initialization is concerned. char[]
is an array of characters, so it follows the above rules. C++17 would choke on using char[]
in a new
expression, but C++20 is fine with it.
If the type-id or new-type-id denotes an array type of unknown bound ([dcl.array]), the new-initializer shall not be omitted; the allocated object is an array with n elements, where n is determined from the number of initial elements supplied in the new-initializer ([dcl.init.aggr], [dcl.init.string]).
Which means that #2 and #3 are supposed to be legal. So GCC is wrong to make them ill-formed. And it makes #1 ill-formed for the wrong reason.