На этом шаге мы закончим рассматривать шаблоны функций.
Перечислим основные свойства параметров шаблона.
template <class type1, class type2>
Соответственно, неверен заголовок:
template <class type1, type2, type3>
template <class t, class t, class t>
//OOР36_1.СРР - параметр шаблона и внешняя переменная с // тем же именем. #include <iostream.h> int N; // Инициализирована по умолчанию нулевым значением. // Функция определяет максимальное из двух значений параметров. template <class N> N max(N x, N y) { N a = x; cout << "\nСчетчик обращений N = " << ++::N; if (a < y) a = y; return a; } void main() { int a = 12, b = 42; max(a,b); float z = 66.3, f = 222.4; max(z,f); }
Результат выполнения программы:
Счетчик обращений N = 1 Счетчик обращений N = 2
Итак, одно имя нельзя использовать для обозначения нескольких параметров одного шаблона, но в разных шаблонах функций могут быть одинаковые имена у параметров шаблонов. Ситуация здесь такая же, как и у формальных параметров при определении обычных функций, и на ней можно не останавливаться подробнее. Действительно, раз действие параметра шаблона заканчивается в конце определения шаблона, то соответствующий идентификатор свободен для последующего использования, в том числе и в качестве имени параметра другого шаблона.
Все параметры шаблона функций должны быть обязательно использованы в спецификациях параметров определения функции. Таким образом, будет ошибочным такой шаблон:
template <class A, class В, class C> В func(A n, С m) {В valu; ... }
В данном неверном примере остался неиспользованным параметр шаблона с именем B. Его применений в качестве типа возвращаемого функцией значения и для определения объекта valu в теле функции недостаточно.
Определяемая с помощью шаблона функция может иметь любое количество непараметризованных формальных параметров. Может быть не параметризовано и возвращаемое функцией значение. Например, в следующей программе шаблон определяет семейство функций, каждая из которых подсчитывает количество нулевых элементов одномерного массива параметризованного типа:
//OOР36_2.СРР - прототип шаблона для семейства функций. #include <iostream.h> template <class D> long count0(int, D *); // Прототип шаблона. void main () { int A[] = {1, 0, 6, 0, 4, 10}; int n = sizeof (A) /sizeof A[0] ; cout << "\ncount0(n,A) = " << count0(n,A); float X[] = {10.0, 0.0, 3.3, 0.0, 2.1}; n = sizeof(X)/sizeof X[0]; cout << "\ncount0(n,X) = " << count0(n,X); } // Шаблон функций для подсчета количества нулевых элементов // в массиве. template <class T> long count0(int size, T* array) { long k = 0; for (int i = 0; i < size; i++) if (int(array[i]) == 0) k++; return k; }
Результат выполнения программы:
count0(n,A) = 2 count0(n,X) = 2
В шаблоне функций count0() параметр T используется только в спецификации одного формального параметра array. Параметр size и возвращаемое функцией значение имеют явно заданные непараметризованные типы.
Как и при работе с обычными функциями, для шаблонов функций существуют определения и описания. В качестве описания шаблона функций используется прототип шаблона:
template <список_параметров_шаблона>
В списке параметров прототипа шаблона имена параметров не обязаны совпадать с именами тех же параметров в определении шаблона. Это и продемонстрировано в программе.
При конкретизации шаблонного определения функции необходимо, чтобы при вызове функции типы фактических параметров, соответствующие одинаково параметризованным формальным параметрам, были одинаковыми. Для определенного выше шаблона функций с прототипом
template <class E> void swap(E,E);
недопустимо использовать такое обращение к функции:
int n = 4; double d = 4.3; swap(n,d); // Ошибка в типах параметров.
Для правильного обращения к такой функции требуется явное приведение типа одного из параметров. Например, вызов:
swap(double(n),d); // Правильные типы параметров.
приведет к конкретизации шаблонного определения функций с параметром типа double.
При использовании шаблонов функций возможна перегрузка как шаблонов, так и функций. Могут быть шаблоны с одинаковыми именами, но разными параметрами. Или с помощью шаблона может создаваться функция с таким же именем, что и явно определенная функция. В обоих случаях "распознавание" конкретного вызова выполняется по сигнатуре, т.е. по типам, порядку и количеству фактических параметров.
На следующем шаге мы рассмотрим шаблоны классов.