Приоритет функций шаблона C++

#include <iostream>

template <class U, class T>
void foo(U&, T&)
{
    std::cout << "first";
}

template <class T>
void foo(int&, const T&)
{
    std::cout << "second";
}

int main()
{
    int a;
    double g = 2.;
    foo(a, g); // prints "first"

    return 0;
}

чтобы вызвать второй foo перегрузка, компилятор должен выполнить только один вывод типа шаблона, но для первой перегрузки он должен выполнить два. Не могли бы вы объяснить, почему вызвана первая перегрузка?

3 ответов


разрешение перегрузки выполняется в несколько шагов.

во-первых, через поиск имени, мы выбираем список жизнеспособных кандидатов. В этом случае, то есть:

template <class U, class T>
void foo(U&, T&);            // with U = int, T = double

template <class T>
void foo(int&, const T&)     // with T = double

Далее мы определяем последовательность преобразования, необходимую для каждого аргумента для каждого жизнеспособного кандидата. Это [конец.микросхема.rank]:

стандартное преобразование последовательности S1 - это лучше, последовательность конвертации, чем стандартное преобразование последовательности S2 if [...]

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

для первого вызова последовательность преобразования (Identity, Identity). Для второго вызова последовательность преобразования (Identity, Identity). Так что здесь мы равны. Ни один из этих пунктов не различает два вызова. Так что мы идем дальше.

  • S1 и S2 являются ссылочными привязками (8.5.3) и не относятся к неявному объектному параметру a нестатическая функция-член, объявленная без ref-квалификатора, и S1 связывает ссылку rvalue к rvalue и S2 связывают ссылку lvalue.

неактуально.

  • S1 и S2 являются ссылочными привязками (8.5.3) , а S1 связывает ссылку lvalue с функцией lvalue и S2 связывает ссылку rvalue с функцией lvalue.

Неа.

  • S1 и S2 отличаются только их квалификационным преобразованием и дают аналогичные типы T1 и T2 (4.4), соответственно, и CV-квалификационная подпись типа T1 является правильным подмножеством cv-квалификации подпись типа T2.

преобразование квалификации-это указатель, нет.

  • S1 и S2 являются ссылочными привязками (8.5.3), и типы, на которые ссылаются ссылки, одинаковы тип, за исключением CV-квалификаторов верхнего уровня, и тип, к которому относится ссылка, инициализированная S2 CV-квалификации, чем тип, к которому ссылка, инициализированная S1, ссылается.

в этом случае, первая перегрузка принимает второй аргумент как double& в то время как вторая перегрузка принимает const double&. Первый меньше cv-квалифицированный, чем последний, поэтому мы останавливаемся здесь-предпочитая foo(U&,T&).

только после шагов, чтобы определить, какая последовательность преобразования лучше, мы переходим к шагу, что более специализированный шаблон предпочтительнее. Полные правила заказа [над.спичка.best] является:

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

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

это то, через что мы только что прошли.

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

вот почему мы забрать foo(U&, T&). Если убрать const, однако, тогда обе последовательности преобразования идентичны во всех шагах-так что в этот момент более специализированный шаблон (foo(int&, T&)) будет выиграно.

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

Также обратите внимание, что шаблонов вычетов не имеет значения. Это мая дело в выборе между перегрузкой, которая является шаблоном, и перегрузкой, которая не является шаблоном , но это не имеет значения при выборе между перегрузкой, которая имеет x параметры шаблона и перегрузка, которая имеет y > x параметры шаблона.


вы объявляете во второй функции, что второй аргумент должен быть const. Приведенный ниже пример вашего с примененной коррекцией вызывает второй:

#include <iostream>

template <class U, class T>
void foo(U&, T&)
{
    std::cout << "first";
}

template <class T>
void foo(int&, T&)
{
    std::cout << "second";
}

int main()
{
    int a;
    double g = 2.;
    foo(a, g);

    return 0;
}

С другой стороны, когда вы явно объявляете второй аргумент, будьте const на main(), приложение вызывает вторую функцию в приведенном выше примере, как и ожидалось:

#include <iostream>

template <class U, class T>
void foo(U&, T&)
{
    std::cout << "first";
}

template <class T>
void foo(int&, const T&)
{
    std::cout << "second";
}

int main()
{
    int a;
    const double g = 2.;
    foo(a, g);

    return 0;
}

наблюдения:

  • первый