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

Почему первый вызов функции (cm(car);) связывается с первой функцией?

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

Если первая функция определена как не-шаблон с фиксированной длиной массива, как:

    void cm(const char (&h)[8]) {cout << "const char (&)[8]" << endl;}

чем снова, он будет выбран по второму (второй вызов будет неоднозначным таким образом).

код:

template<size_t N> void cm(const char (&h)[N]) 
    {std::cout << " const (&)[N] " << endl;}

void cm(const char * h)
    {cout << " const char * " << endl;}

int main()
{
    char car[] = "errqweq";
    const char ccar[] = "errqweq";
    cm(car);
    cm(ccar);
}

Вывод:

 const (&)[N]
 const char * 

Ответ 1

Первый вызов выбирает специализацию шаблона функции - потому что это лучшее совпадение.
Обозначим обе перегрузки:

template<size_t N> void cm(const char (&h)[N])  // (1) - the specialization
    {std::cout << " const (&)[N] " << endl;}

void cm(const char * h)                         // (2)
    {cout << " const char * " << endl;}

Для (1), car привязывается к ссылке. Это преобразование идентичности 1. Для (2) после преобразования от матрица к указателю car, что дает char* 2 необходимо выполнить квалификационное преобразование, чтобы char* становилось char const*, Это теперь вызывает это:

Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если

  • S1 - это правильная подпоследовательность S2 (сравнение последовательностей преобразования в канонической форме, определенных в 13.3.3.1.1, , исключая любые Преобразование Lvalue; последовательность преобразования идентичности рассматривается как подпоследовательность любого нетождественного преобразования последовательность) или, если это не так,
  • [...]

Преобразование от массива к указателю является преобразованием Lvalue, поэтому оно не рассматривается здесь - так же, как и во втором примере. Квалификационная конверсия имеет свою собственную категорию: Квалификационная корректировка. Поэтому преобразование в параметр (1) является подпоследовательностью преобразования в параметр (2): первое является преобразованием идентичности, а второе - квалификационным преобразованием, и согласно вышеприведенному абзацу преобразование идентичности является подпоследовательностью любое преобразование без идентичности. Итак, выбрано (1).

Как вы уже упоминали, во втором случае конверсии одинаково хороши; Вышеприведенная цитата не работает, поскольку преобразование в (2) s параметр не является подпоследовательностью преобразования в параметр (1). Следовательно, применяется [over.match.best]/1.

Учитывая эти определения, жизнеспособная функция F1 определяется как лучше, чем другая жизнеспособная функция F2, если для всех аргументов i, ICSi (F1) не хуже схемы преобразования, чем ICSi (F2), а затем

  • для некоторого аргумента j, ICSj (F1) является лучшей последовательностью преобразования, чем ICSj (F2), или, если не это,
  • контекст представляет собой инициализацию с помощью пользовательского преобразования [...], или, если это не так,
  • F1 - это функция без шаблона, а F2 - специализированная функция шаблона,

Итак, (2) выбирается один. Если шаблон функции не был шаблоном, а функцией с параметром char const (&)[8], вызов был бы неоднозначным как правильно говорит Кланг.


1 [over.ics.ref]/1:

Когда параметр ссылочного типа напрямую связывается (8.5.3) с выражение аргумента, неявная последовательность преобразования - это тождество преобразование, если выражение аргумента не имеет тип, который является производный класс типа параметра, и в этом случае неявный Последовательность преобразования представляет собой преобразование с производной базой (13.3.3.1).

[dcl.init.ref]/5 (что в 8.5.3):

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


2 [conv.array]:

lvalue или rvalue типа "массив N T" или "массив неизвестных bound of T" может быть преобразован в prvalue типа "указатель на T". Результатом является указатель на первый элемент массива.

T может быть cv-квалифицированным, и так будет тип получателя. Здесь T является просто char, поэтому указатель имеет указатель типа на char = > char*.

Ответ 2

Поскольку строка "errqweq" , непосредственно написанная в коде, считывается только потому, что во время выполнения она находится в части "защищена", поскольку она управляется как константа.

Указание на него с помощью const char* ccar; или const char ccar[]; верное. Вы указываете на память, содержащую исходный "errqweq" со спецификатором const: компилятор гарантирует, что строка не будет изменена.

Но посмотрите: char car[] = "errqweq";

Чтобы предоставить вам модифицируемый буфер (как вы запрашиваете без модификатора const), компилятор создает массив из 8 элементов (7 символов +\0) в стеке, копируя в нем (то есть: инициализируя его) строку "errqweq" .

Итак, первый вызов использует аргументы char buffer[8], которые безопасно преобразуются в const char buffer[8]. Очевидно, что фиксированный размер массива дает лучшее совпадение с шаблоном вместо более "слабого" связывания с функцией, которая требует "просто" постоянного указателя.