Проблема в том, что, глядя на тип параметра функции func()
, невозможно определить уникальный тип для аргумента шаблона. Вызов функции func()
мог бы создать экземпляр версии функции compare()
, получающей целые числа или версию, получающую строки. Поскольку невозможно идентифицировать уникальный экземпляр для аргумента функции func()
, этот вызов не будет откомпилирован.
Неоднозначность вызова функции func()
можно устранить при помощи явных аргументов шаблона:
//
func(compare
Это выражение вызывает версию функции func()
, получающую указатель на функцию с двумя параметрами типа const int&
.
Чтобы лучше понять дедукцию типа, рассмотрим такой вызов функции где параметр функции p
является ссылкой на параметр типа шаблона T
:
template
Обратите внимание на два момента: здесь применяются обычные правила привязки ссылок; и спецификаторы const
здесь нижнего уровня, а не верхнего.
Когда параметр функции представляет собой обычную ссылку (l-значение) на параметр типа шаблона (т.е. имеющего форму T&
), правила привязки гласят, что передавать можно только l-значения (например, переменная или выражение, возвращающее ссылочный тип). Этот аргумент может быть или не быть константным. Если аргумент будет константой, то тип Т
будет выведен как константный:
template
//
//
f1(i); //
f1(ci); //
f1(5); //
//
Если параметр функции имеет тип const Т&
, обычные правила привязки гласят, что можно передать любой вид аргумента — объект (константный или нет), временный объект или литеральное значение. Когда сам параметр функции является константой, выведенный для параметра Т
тип не будет константным типом. Константность является частью типа параметра функции, и поэтому она не становится также частью типа параметра шаблона:
template
//
//
//
f2(i); //
f2(ci); //
f2(5); //
//
Когда параметр функции является ссылкой на r-значение (см. раздел 13.6.1), т.е. имеет форму Т&&
, обычные правила привязки гласят, что этому параметру можно передать r-значение. При этом дедукция типа ведет себя таким же образом, как дедукция обычного ссылочного параметра функции на l-значение. Выведенный тип для параметра Т
— это тип r-значения:
template
f3(42); //
//
Предположим, что i
является объектом типа int
. Можно подумать, что такой вызов, как f3(i)
, будет недопустим. В конце концов, i
— это l-значение, а ссылку на r-значение обычно нельзя связать с l-значением. Однако язык определяет два исключения из обычных правил привязки, которые позволяют это. На этих исключениях из правил основан принцип работы таких библиотечных функций, как move()
.