Чтобы избежать путаницы, я очень хорошо понимаю разницу между массивами и указателями, концепцию распада к указателю и концепцию передачи массива с помощью ссылка в 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;
}
В этом случае программа компилируется и выбирается перегрузка, которая принимает ссылку на массив. Однако я не понимаю, почему это решение должно быть необходимым. Я хотел бы понять, почему компилятор считает функцию, для которой указатель на затухание является равноправным вероятным кандидатом на перегрузку, в качестве ссылки на массив, когда передаваемый аргумент представляет собой массив.
первая перегрузка точно соответствует типу, тогда как вторая перегрузка требует дополнительного шага затухания до указателя.
Потому что при проверке ранжирование последовательностей неявного преобразования в разрешение перегрузки, преобразование массива в указатель считается точным соответствием, поэтому вторая перегрузка имеет тот же ранг, что и первая.
Из стандарта, $ 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-й перегрузки) также «Точное совпадение».
Других решений пока нет …