Подходящей для этого вызова является только первая версия функции debug_rep()
. Второй версии требуется параметр в виде указателя, а в этом вызове передан не указатель. Нет никакого способа создать экземпляр шаблона функции, ожидающего тип указателя, из параметра, который не является указателем, поэтому дедукция аргумента терпит неудачу. Поскольку есть только одна подходящая функция, она и используется.
Если происходит вызов функции debug_rep()
с указателем:
cout << debug_rep(&s) << endl;
то обе функции создают подходящие экземпляры:
• debug_rep(const string*&)
— экземпляр первой версии функции debug_rep()
с привязкой параметра Т
к типу string*
;
• debug_rep(string*)
— экземпляр второй версии функции debug_rep()
с привязкой параметра Т
к типу string
.
Точным соответствием для этого вызова является экземпляр второй версии функции debug_rep()
. Создание экземпляра первой версии требует преобразования простого указателя в указатель на константу. Обычный подбор функции гласит, что следует предпочесть второй шаблон, и в действительности так и происходит.
В качестве другого примера рассмотрим следующий вызов:
const string *sp = &s
cout << debug_rep(sp) << endl;
Здесь подходящими являются оба шаблона, и оба обеспечивают точное соответствие:
• debug_rep(const string*&)
— экземпляр первой версии шаблона с привязкой параметра Т
к типу const string*
;
• debug_rep(const string*)
— экземпляр второй версии шаблона с привязкой параметра Т
к типу const string
.
В данном случае обычный подбор функции не может различить эти два вызова. Можно было бы ожидать, что этот вызов будет неоднозначен. Однако благодаря специальному правилу для перегруженных шаблонов функций этот вызов решается как debug_rep(Т*)
, поскольку это более специализированный шаблон.
Причина для этого правила в том, что без него не было бы никакого способа вызвать версию функции debug_rep()
для указателя на константу. Проблема в том, что к шаблону debug_rep(const Т&)
подходит практически любой тип, включая типы указателя. Этот шаблон является более общим, чем debug_rep(Т*)
, который может быть вызван только для типов указателя. Без этого правила вызовы с передачей указателей на константу всегда будут неоднозначны.
Для следующего примера определим обычную, не шаблонную версию функции debug_rep()
, выводящую строки в двойных кавычках:
//
string debug_rep(const string &s) {
return '"' + s + '"';
}
Теперь, когда происходит вызов функции debug_rep()
для строки:
string s("hi");
cout << debug_rep(s) << endl;
есть две одинаково хорошо подходящих функции:
• debug_rep
— первый шаблон с привязкой параметра T
к типу string
;
• debug_rep(const string&)
— обычная, не шаблонная функция.
В данном случае у обеих функций одинаковый список параметров, поэтому каждая из них обеспечивает одинаково хорошее соответствие этому вызову. Однако выбирается нешаблонная версия. По тем же причинам, по которым предпочитаются наиболее специализированные из одинаково хорошо подходящих шаблонов функций, нешаблонная функция предпочитается при одинаково хорошем соответствии с шаблонной функцией.
До сих пор не рассматривался случай с указателями на символьные строки в стиле С и строковые литералы. Теперь, когда имеется версия функции debug_rep()
, получающая строку, можно было бы ожидать, что ей будет соответствовать вызов, которому переданы символьные строки. Однако рассмотрим этот вызов:
cout << debug_rep("hi world!") << endl; //
Здесь подходящими являются все три функции debug_rep()
:
• debug_rep(const Т&)
— с привязкой параметра Т
к типу char[10]
;
• debug_rep(Т*)
— с привязкой параметра Т
к типу соnst char
;
• debug_rep(const string&)
— требующая преобразования из const char*
в string
.