Правила привязки аргумента функции для передачи массива по ссылке против передаваемого указателя
Чтобы избежать путаницы, я очень хорошо понимаю разницу между массивами и указателями, концепцию затухания в указатель и концепцию передачи массива по ссылке в 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 ответ
первая перегрузка точно соответствует типу, тогда как вторая перегрузка требует дополнительного шага затухания до указателя.
Поскольку при проверке ранжирования последовательностей неявного преобразования в разрешении перегрузки преобразование массива в указатель рассматривается как точное совпадение, поэтому вторая перегрузка имеет такой же ранг, что и первая.
Из стандарта: $16.3.3.1.1 Стандартные последовательности преобразования [over.ics.scs] Таблица 13 - Преобразования
Conversion Category Rank Subclause No conversions required Identity Exact Match ... ... Array-to-pointer conversion Lvalue Transformation Exact Match [conv.array] ... ...
Стоит отметить, что ранг "Преобразования не требуются" (т. Е. В случае 1-й перегрузки) также "Точное совпадение".