GSi
GSi

Reputation: 649

Initializer_list as argument to an array reference parameter in a not-template context

My question concerns this very simple and short code where an overload resolution is attempted between two non-template functions accepting an array reference parameter. The question has been posted elsewhere, but in a template deduction context. Here's the code:

#include <iostream>

void foo ( const int (&x) [3] ) { std::cout << "3\n"; }
void foo ( const int (&x) [2] ) { std::cout << "2\n"; }

int main()
{
    foo({1,2,3});
}

g++ 4.8.3 compiles this code selecting the first function as (I suppose) the ONLY viable, while clang 3.4 doesn't compile it, saying that the call to foo is ambiguous (why?).

Which compiler does the right thing?

clang doesn't compile the code even removing the second overload: it seems that an initializer_list is simply not accepted to initialize an array reference.

Is this buggy?

Upvotes: 13

Views: 660

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171403

DR 1232 changed C++11 to allow calling functions with reference-to-array parameters from an initializer list. (The changes to the working paper are shown in N3262.) All the compilers tested implement that rule.

DR1307 then changed the same wording again to resolve the ambiguity you've discovered. Your code is accepted by GCC and EDG compilers, so I assume Clang does not implement that DR yet.

It's interesting to note that even after that DR was resolved, foo({1, 2}) is still ambiguous, and rejected by all compilers. The reason is that {1, 2} can bind to the const int(&)[2] parameter (obviously) but it can also bind to the const int(&)[3] parameter because int is default-constructible, so it means the same as {1, 2, int()} i.e. {1, 2, 0}

Upvotes: 4

user743382
user743382

Reputation:

I think that although GCC's behaviour is more useful, it's clang's behaviour that is correct for C++11:

13.3.3.1.5 List-initialization sequence [over.ics.list]

2 If the parameter type is std::initializer_list<X> or "array of X" and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X.

This conversion sequence pays no attention to the array length. Both function overloads give an implicit conversion sequence that is an identity conversion: both take a reference to an array of int, and each element in the function argument is an int.

Overload resolution then sees two identity conversions, and although the standard does have a few exceptions for resolving conflicts on conversions of equal rank, there is none that pays attention to the array's length:

13.3.3.2 Ranking implicit conversion sequences [over.ics.rank]

3 Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:

followed by a list that does not mention arrays at all.

Jonathan Wakely points out that this has since changed. Your question is exactly what prompted that change, and the corresponding DR is #1307. In C++14, your code is valid, but not in C++11.

Upvotes: 6

Related Questions