Подтвердить что ты не робот

Initializer_list как аргумент для ссылочного параметра массива в контексте не-шаблона

Мой вопрос касается этого очень простого и короткого кода, в котором производится попытка перегрузки между двумя функциями без шаблона, принимающими параметр ссылки на массив. Вопрос был опубликован в другом месте, но в контексте шаблона вывода. Здесь код:

#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 компилирует этот код, выбирая первую функцию как (я полагаю) ТОЛЬКО жизнеспособную, в то время как clang 3.4 не компилирует его, говоря, что вызов foo неоднозначен (почему?).

Какой компилятор делает правильные вещи?

clang не компилирует код, даже удаляя вторую перегрузку: кажется, что initializer_list просто не принят для инициализации ссылки массива.

Является ли это ошибкой?

4b9b3361

Ответ 1

Я думаю, что хотя поведение GCC более полезно, это поведение clang корректно для С++ 11:

13.3.3.1.5 Последовательность инициализации списка [over.ics.list]

2 Если тип параметра std::initializer_list<X> или "array of X", и все элементы списка инициализатора могут быть неявно преобразованы в X, неявная последовательность преобразования является наихудшим преобразованием, необходимым для преобразования элемента списка X.

Эта последовательность преобразования не обращает внимания на длину массива. Обе функции перегрузки дают неявную последовательность преобразований, которая является преобразованием идентичности: оба берут ссылку на массив из int, и каждый элемент в аргументе функции является int.

Разрешение перегрузки затем видит два преобразования идентичности, и хотя в стандарте есть несколько исключений для разрешения конфликтов при преобразованиях равного ранга, никто не обращает внимания на длину массива:

13.3.3.2 Ранжирование неявных последовательностей преобразования [over.ics.rank]

3 Две неявные последовательности преобразования одной и той же формы являются неотличимыми последовательностями преобразования, если не применяется одно из следующих правил:

а затем список, который вообще не упоминает массивы.

Джонатан Вакели указывает, что с тех пор это изменилось. Ваш вопрос именно то, что вызвало это изменение, и соответствующий DR #1307. В С++ 14 ваш код действителен, но не в С++ 11.

Ответ 2

DR 1232 изменил С++ 11, чтобы разрешить вызывающие функции с параметрами reference-to-array из списка инициализаторов. (Изменения в рабочем документе показаны в N3262.) Все тестируемые компиляторы реализуют это правило.

DR1307 затем снова изменил ту же формулировку, чтобы устранить двусмысленность, которую вы обнаружили. Ваш код принят компиляторами GCC и EDG, поэтому я полагаю, что Clang еще не реализует DR.

Интересно отметить, что даже после того, как DR был разрешен, foo({1, 2}) по-прежнему неоднозначен и отклонен всеми компиляторами. Причина в том, что {1, 2} может привязываться к параметру const int(&)[2] (очевидно), но он также может привязываться к параметру const int(&)[3], потому что int по умолчанию является конструктивным, поэтому он означает то же самое, что и {1, 2, int()} т.е. {1, 2, 0}