Упражнение 16.59. С учетом того, что s
имеет тип string
, объясните вызов svec.emplace_back(s)
.
Упражнение 16.60. Объясните, как работает функция make_shared()
(см. раздел 12.1.1).
Упражнение 16.61. Определите собственную версию функции make_shared()
.
Не всегда можно написать один шаблон, который наилучшим образом подходит для всех возможных типов аргументов шаблона, для которых может быть создан его экземпляр. В некоторых случаях общий шаблон просто не подходит для типа: он либо приводит к ошибке при компиляции, либо к неправильным действиям. С другой стороны, иногда можно воспользоваться уникальными возможностями определенного типа для создания более эффективной функции, чем та, которой снабжен экземпляр общего шаблона.
Функция compare()
— хороший пример шаблона функции, общее определение которого не подходит для специфического типа, а именно символьных указателей. Хотелось бы, чтобы функция compare()
сравнивала символьные указатели, используя функцию strcmp()
, а не сравнивала значения указателей. Действительно, ведь уже есть перегруженная функция compare()
, обрабатывающая символьные строковые литералы (см. раздел 16.1.1):
//
template
//
template
int compare(const char (&)[N], const char (&)[M]);
Однако версия функции compare()
с двумя параметрами значения шаблона будет вызвана только при передаче строкового литерала или массива. Если происходит вызов функции compare()
с символьными указателями, будет вызвана первая версия шаблона:
const char *p1 = "hi", *p2 = "mom";
compare(p1, p2); //
compare("hi", "mom"); //
Нет никакого способа преобразовать указатель в ссылку на массив, поэтому вторая версия функции compare()
не подходит для передачи указателей p1
и p2
как аргументов.
Для обработки символьных указателей (в отличие от массивов) можно определить compare()
. Специализация — это отдельное определение шаблона, в котором определяется один или несколько параметров шаблона для получения специфического типа.
При специализации шаблона функции следует предоставить аргументы для каждого параметра первоначального шаблона. Для указания специализации шаблона используется ключевое слово template
, сопровождаемое парой пустых угловых скобок (<>
). Пустые скобки означают, что аргументы будут предоставлены для всех параметров первоначального шаблона:
//
//
template <>
int compare(const char* const &p1, const char* const &p2) {
return strcmp(p1, p2);
}
Трудная для понимания часть этой специализации относится к типам параметра функции. При определении специализации типы параметров функции должны совпадать с соответствующими типами ранее объявленного шаблона:
template
В этой специализации параметры функции являются ссылками на константные типы. Подобно псевдонимам типа, взаимодействие между типами параметра шаблона, указателями и константами может удивить (см. раздел 2.5.1).
Необходимо определить специализацию шаблона этой функции с типом const char*
для параметра Т
. Функция потребует ссылки на константную версию этого типа. Константная версия типа указателя — это константный указатель, а не указатель на константу (см. раздел 2.4.2). В данной специализации следует использовать тип const char* const &
, являющийся ссылкой на константный указатель на константный символ.
При определении специализации шаблона функции разработчик, по существу, выполняет задачу компилятора. Таким образом, определение предоставляется для использования специфического экземпляра первоначального шаблона. Важно понимать, что специализация — это создание экземпляра функции; а не перегрузка ее экземпляра.