Правила привязки аргументов функции для передачи массива по ссылке vs указатель передачи

чтобы избежать путаницы, я очень хорошо понимаю разницу между массивами и указателями, концепцию распада на указатель и концепцию передачи массива ссылка в C++, и т. д.

мой вопрос здесь конкретно о правилах, используемых компилятором для выбора функции из набора функций перегрузка кандидаты, когда одна перегрузка принимает ссылку на массив, а другая перегрузка принимает указатель.

например, предположим, что у нас есть:

template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
    std::cout << "Array-reference overload!" << std::endl;
}

template <class T>
void foo(const T* ptr)
{
    std::cout << "Pointer overload!" << std::endl;
}

если мы попытаемся вызвать шаблон функции foo() следующим образом:

const char arr[2] = "A";
foo(arr);

... тогда я ожидал бы, что первый перегрузка, которая принимает ссылку на массив, будет выбран компилятором.

однако, используя GCC 4.9.2, если я скомпилирую вышеуказанный код, я получаю ошибку:

test.cpp:28:9: error: call of overloaded ‘foo(const char [2])’ is ambiguous

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

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

template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
    std::cout << "Array-reference overload!" << std::endl;
}

template <class T>
void foo(T ptr, typename std::enable_if<std::is_pointer<T>::value>::type* = 0)
{
    std::cout << "Pointer overload!" << std::endl;
}

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

1 ответов


первая перегрузка точно соответствует типу, тогда как вторая перегрузка требует дополнительного шага распада к указателю.

потому что при проверке рейтинг неявное преобразование последовательности на перегрузка разрешение на преобразование массива в указатель считается точным совпадением, таким образом, 2-я перегрузка имеет тот же ранг с 1-й.

из стандартного, $16.3.3.1.1 стандарт последовательности преобразования [окончание.микросхема.scs] таблица 13-преобразования

Conversion                   Category               Rank         Subclause
No conversions required      Identity               Exact Match
... ...
Array-to-pointer conversion  Lvalue Transformation  Exact Match  [conv.array]
... ...

стоит отметить, что ранг " нет необходимых преобразований "(т. е. случай для 1-й перегрузки) также является" точным совпадением".