Разность функциональных шаблонов
При попытке перегрузить шаблон функции и функции я получаю другое поведение. Для функции:
void foo(int)
{
std::cout << "int";
}
void foo(char)
{
std::cout << "char";
}
foo(42)
int
. Но для шаблона функции:
template <int T>
void bar()
{
std::cout << "int T";
}
template <char T>
void bar()
{
std::cout << "char T";
}
bar<42>()
- неоднозначный вызов. Это происходит, даже если я использую char, например bar<'a'>()
. Почему одно работает, а не другое?
Ответы
Ответ 1
Стандартная N4140 (кредит идет M.M) дает это объяснение и образец в 14.8.2 Вывод аргумента шаблона:
9 За исключением случаев, описанных выше, использование недопустимого значения не должно приводить к вычету типа. [Пример: В следующем примере 1000 преобразуется в signed char
и приводит к определенному реализацией значению как указано в (4.7). Другими словами, оба шаблона считаются даже при 1000, когда они преобразуются в подписанный char, приводит к определенному реализацией значению.
template <int> int f(int);
template <signed char> int f(int);
int i1 = f<1>(0); // ambiguous
int i2 = f<1000>(0); // ambiguous
- конец примера]
Однако обратите внимание, что в последующих черновиках правила изменяются потому что:
Это уже не корректно, даже игнорируя тот факт, что некоторые реализации могут представлять значение 1000 в качестве аргументов signed char
: integer и перечисления, не являющихся типом шаблонов, теперь преобразуются в константные выражения (14.3.2 [temp. arg.nontype], пункт 1), а преобразованные константные выражения запрещают сужение конверсий (5.20 [expr.const], пункт 3).
Предлагаемый образец:
template <int> int f(int);
template <signed char> int f(int);
int i1 = f<1000>(0); // OK
int i2 = f<1>(0); // ambiguous; not narrowing
Ответ 2
Существует разница между выборами перегрузки и специализаций. Разрешение перегрузки определяет неявные преобразования. Для преобразования 42
в int
преобразования не требуется преобразование, поэтому это Exact Match, которое превосходит неявное преобразование, необходимое для 42
- char
.
С другой стороны, специализация "перегрузка" в шаблоне функций использует правила частичного заказа, в которой используется вычитание аргумента шаблона, чтобы определить, что является более "специализированным" чем другой. Не вдаваясь в подробности, вычет не прерывается для int
или char
со значением 42
, поэтому они оба одинаково специализированы. Если бы вы использовали значение вне диапазона подписанного char, i.e 128
, вывод был бы неудачным, и была выбрана специализация int
.
Ответ 3
В соответствии с этим веб-сайтом, который содержит очень хорошие примеры и объяснения, "специализация шаблонов функций не перегружает". Вот почему вы получаете call to 'bar' is ambiguous
.