Validity of zero-size arrays
[dcl.array] p1 states that:
[The constant-expression] N
specifies the array bound, i.e., the number of elements in the array; N
shall be greater than zero.
Zero-size arrays are thus disallowed in principle.
Note that your zero-size array appears in a function parameter, and this may be relevant according to [dcl.fct] p5:
After determining the type of each parameter, any parameter of type “array of T
” or of function type T
is adjusted to be “pointer to T
”.
However, this type adjustment rule only kicks on after determining the type of the parameters, and one parameter has type Char<I>[0]
.
This should disqualify the first overload from being a candidate.
In fact, your program is IFNDR because no specialization of y
would be well-formed (see [temp.res.general] p6).
It is not totally clear from the wording, but the first overload would be ill-formed despite the type adjustment, and both GCC and clang agree on this (see -pedantic-errors
diagnostic triggering for char[0]
parameters).
Even if the compiler supports zero-size arrays as an extension, this isn't allowed to affect valid overload resolution according to [intro.compliance.general] p8:
A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program.
Implementations are required to diagnose programs that use such extensions that are ill-formed according to this document.
Having done so, however, they can compile and execute such programs.
Conclusion
Your program is IFNDR because no specialization of the first overload of y
is valid.
All compilers are correct through their own extensions.
However, if we assume that the first overload of y
is valid, then it should not be a viable candidate during the call y<N>()
, and should be removed from the overload set, even if zero-size arrays are supported as a compiler extension.
Only clang implements this correctly.
Interaction of default arguments and overload resolution
In this section, let's assume that zero-size arrays were allowed.
This is just for the sake of understanding the observed compiler behavior better.
Then hypothetically, a call y<N>(0)
is unambiguous, and all compilers agree and call the int
overload.
This is because int
requires no conversions, but a conversion from 0
to a pointer type would require pointer conversion.
Overload resolution does not consider default arguments; see Are default argument conversions considered in overload resolution?.
Thus hypothetically, both overloads of y
are viable candidates for y<N>()
and neither is a better match because neither is more specialized according to the rule of partial ordering of function templates. This is GCC's behavior.
Note: both GCC's and Clang's behavior can be explained, where Clang is more correct looking past the IFNDR issue. I am unable to explain ICC's behavior; it makes no sense.
[0]
to[I-5]
, icc will choose int version. – Smithereens= 0
means when it comes after a type rather than the name of an argument.int foo = 0
in a function declaration makes sense, sincefoo
is a parameter that can have a default value of zero. Butint = 0
makes no sense to me, sinceint
is a type, and there's no parameter of typeint
that can be set to zero. I don't know if there's some obscure C++ rule that allows it, but it at least looks wrong. – Calaveriteint = 0
is the default value for a parameter without a name. Limited usefulness, except in made up test cases. – Hemimorphic