Разрешение перегрузки шаблонных функций

Рассмотрите этот код:

#include <iostream>

//Number1
template<typename T1, typename T2>
auto max (T1 a, T2 b)
{
    std::cout << "auto max(T1 a, T2 b)" <<std::endl;
    return  b < a ? a : b;
}

//Number2
template<typename RT, typename T1, typename T2>
RT max (T1 a, T2 b)
{
    std::cout << "RT max(T1 a, T2 b)" << std::endl;
    return  b < a ? a : b;
}


int main()
{
    auto a = ::max(4, 7.2);         //Select Number1

    auto b = ::max<double>(4, 7.4); //Select Number2

    auto c = ::max<int>(7, 4.);     //Compile-time error overload ambiguous

    auto c = ::max<double>(7, 4.); //Select Number2

}

auto c = ::max<int>(7, 4.);: эта строка не компилируется из-за неоднозначности перегрузки со следующим сообщением:

maxdefault4.cpp:9:27: error: call of overloaded 'max(int, double)' is ambiguous
  auto c = ::max<int>(7, 4.);
                           ^
maxdefault4.cpp:9:27: note: candidates are:
In file included from maxdefault4.cpp:1:0:
maxdefault4.hpp:4:6: note: auto max(T1, T2) [with T1 = int; T2 = double]
 auto max (T1 a, T2 b)
      ^
maxdefault4.hpp:11:4: note: RT max(T1, T2) [with RT = int; T1 = int; T2 = double]
 RT max (T1 a, T2 b)
    ^

пока следующий код: àuto c = ::max<double>(7, 4.) успешно, почему у нас не появляется одно и то же сообщение об ошибке, в котором говорится, что вызов неоднозначен для max<double> так же, как и при сбое max<int>?

Почему для double нет проблем?

В книге "Шаблоны C++, полное руководство" я читал, что при выводе аргумента шаблона не учитывается тип возвращаемого значения, так почему max<int> является неоднозначным, а не max<double>?

Действительно ли возвращаемый тип шаблонной функции не учитывается при вычислении аргумента?

Ответ 1

  вычет аргумента шаблона не учитывает тип возвращаемого значения,

Да. Вывод аргумента шаблона выполняется на основе аргументов функции.

так почему max<int> неоднозначен, а не max<double>?

Учитывая ::max<int>(7, 4.), для 1-й перегрузки 1-й параметр шаблона T1 указан как int, а T2 выводится как double из 2-го аргумента функции 4., тогда экземпляр будет double max(int, double). Для 2-й перегрузки первый параметр шаблона RT задается как int, T1 выводится как int из 7, T2 выводится как double из 4., а затем создается экземпляр будет int max(int, double). Разрешение перегрузки также не учитывает тип возвращаемого значения, обе перегрузки являются точным соответствием, а затем неоднозначными.

Учитывая ::max<double>(7, 4.), для 1-й перегрузки первый параметр шаблона T1 указан как double, а T2 выводится как double из 4., поэтому создание экземпляра будет double max(double, double). Для 2-й перегрузки первый параметр шаблона RT задается как double, T1 выводится как int из 7, T2 выводится как double из 4., а затем создается экземпляр будет double max(int, double). Затем вторая перегрузка побеждает в разрешении перегрузки, поскольку она точно соответствует, 1-й требует неявного преобразования из int в double для первого аргумент 7.

Ответ 2

Давайте посмотрим, что указывает double в качестве аргумента для компилятора во время разрешения перегрузки.

Для шаблона "Number1" max он указывает, что первый аргумент должен иметь тип double. При попытке сопоставления с шаблоном компилятор определяет, что второй аргумент имеет тип double. Таким образом, результирующая подпись - auto max(double, double). Это совпадение, хотя оно предполагает приведение первого аргумента из int в double.

Для шаблона "Number2" max он указывает, что тип возвращаемого значения - double. Типы аргументов выводятся. Таким образом, результирующая подпись - double max(int, double). Это точное совпадение, устраняющее любую неопределенность.

Теперь давайте посмотрим на указание int. Теперь две подписи - auto max(int, double) и double max(int, double). Как видите, нет разницы, которая имеет отношение к разрешению перегрузки, что приводит к неоднозначности.

По сути, передав double, вы отравили одну из перегрузок, вызвав ненужное преобразование; тем самым перегрузка становится доминирующей. Переход в int, напротив, не ограничивает перегрузочную способность как идеальное совпадение.

Ответ 3

Для каждого из ваших вызовов функций компилятор имеет 2 функции на выбор и выбирает лучшую. Неизвестные параметры шаблона выводятся из аргументов, кроме RT, который должен быть явно указан и не может быть выведен.

auto a = ::max(4, 7.2);

Поскольку RT не указан и не может быть выведен, вторая перегрузка не используется, поэтому игнорируется. Первый выбран, и типы выводятся как int и double.

auto b = ::max<double>(4, 7.4);

Теперь указывается RT, поэтому компилятор может выбрать либо использование max<double,int,double>, либо max<double, double>, типы аргументов для версии параметра 3-шаблона точно соответствуют аргументам функции, тогда как версия параметра 2-шаблона требует преобразования из int - double, поэтому выбрана перегрузка с тремя параметрами.

auto c = ::max<int>(7, 4.);

RT теперь указывается, поэтому компилятор может выбрать либо использование max<int,int,double>, либо max<int, double>, типы аргументов обеих функций теперь одинаковы, поэтому компилятор не может выбирать между ними.