Упражнение 16.30. Повторно выполните некоторые из своих предыдущих программ, чтобы проверить собственные переделанные классы shared_ptr
и Blob
. (Примечание: реализация типа weak_ptr
не рассматривается в этом издании, поэтому не получится использовать класс BlobPtr
с пересмотренным классом Blob
.)
Упражнение 16.31. Объясните, как компилятор мог бы встроить вызов функции удаления, если бы с классом unique_ptr
был использован класс DebugDelete
.
16.2. Дедукция аргумента шаблона
Как уже упоминалось, для определения параметров шаблона для шаблона функции компилятор по умолчанию использует аргументы в вызове. Процесс определения аргументов шаблона по аргументам функции называется дедукцией аргумента шаблона (template argument deduction). В ходе дедукции аргумента шаблона компилятор использует типы аргументов вызова для поиска таких аргументов шаблона, которые обеспечат лучшее соответствие создаваемой версии функции для данного вызова.
Подобно нешаблонным функциям, передаваемые в вызове шаблона функции аргументы используются для инициализации параметров этой функции. Параметры функции, тип которых использует параметр типа шаблона, имеют специальные правила инициализации. Только очень ограниченное количество автоматических преобразований применимо к таким аргументам. Вместо преобразования аргументов компилятор создает новые экземпляры.
Как обычно, спецификаторы const
верхнего уровня (см. раздел 2.4.3) в параметре или аргументе игнорируются. Единственными остальными преобразованиями, выполняемыми при вызове шаблона функции, являются следующие.
• Преобразования констант: параметр функции, являющийся ссылкой (или указателем) на константу, может быть передан как ссылка (или указатель) на не константный объект (см. раздел 4.11.2).
• Преобразование массива или функции в указатель: если тип параметра функции не будет ссылочным, то к аргументам типа массива или функции будет применено обычное преобразование указателя. Аргумент типа массива будет преобразован в указатель на его первый элемент. Точно так же аргумент типа функции будет преобразован в указатель на тип функции (см. раздел 4.11.2).
Другие преобразования, такие как арифметические преобразования (см. раздел 4.11.1), преобразования производного в базовый (см. раздел 15.2.2) и пользовательские преобразования (см. разделы 7.5.4 и 14.9) не выполняются.
В качестве примера рассмотрим вызовы функции fobj()
и fref()
. Функция fobj()
копирует свои параметры, тогда как параметры функции fref()
являются ссылками:
template
template
string s1("a value");
const string s2("another value");
fobj(s1, s2); //
fref(s1, s2); //
//
int а[10], b[42];
fobj(a, b); //
fref(a, b); //
В первой паре вызовов как аргументы передаются строка и константная строка. Даже при том, что эти типы не соответствуют точно друг другу, оба вызова допустимы. В вызове функции fobj()
аргументы копируются, поэтому не имеет значения, был ли первоначальный объект константой. В вызове функции fref()
тип параметра — ссылка на константу. Преобразование в константу для ссылочного параметра является разрешенным преобразованием, поэтому данный вызов допустим.
В следующей паре вызовов как аргументы передаются массивы, отличающиеся размером, а следовательно, имеющие разные типы. В вызове функции fobj()
различие типов массивов не имеет значения. Оба массива преобразуются в указатели. Типом параметра шаблона в функции fobj
является int*
. Вызов функции fref()
, однако, недопустим. Когда параметр является ссылкой, массивы не преобразовываются в указатели (см. раздел 6.2.4). Типы а
и b
не совпадают, поэтому вызов ошибочен.