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

Чтобы избежать путаницы, я очень хорошо понимаю разницу между массивами и указателями, концепцию затухания в указатель и концепцию передачи массива по ссылке в 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-й перегрузки) также "Точное совпадение".

Другие вопросы по тегам