Шаг 36.
Шаблоны функций (окончание)

    На этом шаге мы закончим рассматривать шаблоны функций.

    Перечислим основные свойства параметров шаблона.

  1. Имена параметров шаблона должны быть уникальными во всем определении шаблона.

  2. Список параметров шаблона функций не может быть пустым, так как при этом теряется возможность параметризации и шаблон функций становится обычным определением конкретной функции.

  3. В списке параметров шаблона функций может быть несколько параметров. Каждый из них должен начинаться со служебного слова class. Например, допустим такой заголовок шаблона:
                 template <class type1, class type2>
    

        Соответственно, неверен заголовок:

                 template <class type1, type2, type3>
    


  4. Недопустимо использовать в заголовке шаблона параметры с одинаковыми именами, т.е. ошибочен такой заголовок:
                template <class t, class t, class t>
    


  5. Имя параметра шаблона (в наших примерах type1, type2 и т.д.) имеет в определяемой шаблоном функции все права имени типа, т.е. с его помощью могут специализироваться формальные параметры, определяться тип возвращаемого функцией значения и типы любых объектов, локализованных в теле функции. Имя параметра шаблона видно во всем определении и скрывает другие использования того же идентификатора в области, глобальной по отношению к данному шаблону функций. Если внутри тела определяемой функции необходим доступ к внешним объектам с тем же именем, нужно применять операцию изменения области видимости. Следующая программа иллюстрирует указанную особенность имени параметра шаблона функций:
//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.

    При использовании шаблонов функций возможна перегрузка как шаблонов, так и функций. Могут быть шаблоны с одинаковыми именами, но разными параметрами. Или с помощью шаблона может создаваться функция с таким же именем, что и явно определенная функция. В обоих случаях "распознавание" конкретного вызова выполняется по сигнатуре, т.е. по типам, порядку и количеству фактических параметров.

    На следующем шаге мы рассмотрим шаблоны классов.




Предыдущий шаг Содержание Следующий шаг