Правила привязки аргументов функции для передачи массива по ссылке 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-й перегрузки) также является" точным совпадением".