Для обозначения параметра типа шаблона интуитивно понятней использовать ключевое слово typename
, а не class
; в конце концов, для фактического типа параметра вполне может быть использован встроенный тип, а не только класс. Кроме того, ключевое слово typename
более точно указывает на то, что следующее за ним имя принадлежит типу. Однако ключевое слово typename
было добавлено в язык С++ как часть стандарта С++, поэтому в устаревших программах, вероятнее всего, осталось исключительно ключевое слово class
.
Кроме параметров типа, в определении шаблона могут быть использованы class
или typename
используются имена типов.
При создании экземпляра шаблона такие параметры заменяются значением, предоставленным пользователем или выведенным компилятором. Чтобы компилятор смог создать экземпляр шаблона во время компиляции, эти значения должны быть константными выражениями (см. раздел 2.4.4).
В качестве примера напишем версию функции compare()
, работающую со строковыми литералами. Такие литералы представляют собой массивы типа const char
. Поскольку скопировать массив нельзя, определим параметры как ссылки на массив (раздел 6.2.4). Поскольку необходима возможность сравнивать литералы разных длин, снабдим шаблон двумя параметрами значения. Первый параметр шаблона представляет размер первого массива, а второй — размер второго:
template
int compare(const char (&p1)[N], const char (&p2)[M]) {
return strcmp(p1, p2);
}
При вызове следующей версии функции compare()
компилятор будет использовать размер литералов для создания экземпляра шаблона с размерами, которыми заменяют параметры N
и M
:
compare("hi", "mom")
Не забывайте, что компилятор завершает строковый литерал пустым символом (см. раздел 2.1.3). В результате компилятор создаст такой экземпляр:
int compare(const char (&p1)[3], const char (&p2)[4])
Параметр значения может быть целочисленным типом, указателем, ссылкой на объект (l-значением) или на тип функции. Аргумент, связанный с целочисленным параметром значения, должен быть константным выражением. У аргументов, привязанных к указателю или ссылочному параметру значения, должна быть статическая продолжительность существования (см. главу 12). Нельзя использовать обычный (нестатический) локальный или динамический объект как аргумент шаблона для параметра значения шаблона в виде ссылки или указателя. Параметр-указатель может быть также создан как nullptr
или нулевое константное выражение.
Параметр значения шаблона — это константное значение в определении шаблона. Параметр значения применим там, где требуются константные выражения, например, при определении размера массива.
inline
и constexpr
Шаблон функции может быть объявлен как inline
(встраиваемый) или constexpr
, как и обычная функция. Спецификаторы inline
и constexpr
располагаются после списка параметров шаблона, но перед типом возвращаемого значения.
//
template
//
inline template
Продемонстрируем два наиболее важных принципа создания обобщенного кода на примере функции compare()
.
• Параметры функций в шаблоне должны быть ссылками на константу.
• При проверке в теле шаблона следует использовать только оператор сравнения <
.
Объявление параметров функций ссылками на константы гарантирует возможность применения функции к типам, которые не допускают копирования. Большинство типов, включая встроенные типы, но исключая указатели unique_ptr
и типы ввода-вывода, а также все использованные ранее библиотечные типы допускают копирование. Но вполне могут встретиться и другие типы, которые не допускают копирования. Сделав параметры ссылками на константы, можно гарантировать применимость таких типов в функции compare()
. Кроме того, если функция compare()
будет применена для больших объектов, такая конструкция позволит избежать копирования и сэкономит время при выполнении.
Некоторые читатели могут подумать, что для сравнения было бы целесообразней использовать оба оператора <
и >
.
//
if (v1 < v2) return -1;
if (v1 > v2) return 1;