Тема №12 Шаблоны. Шаблоны функций

Шаблоны, которые называют иногда родовыми или параметризованными типами, позволяют создавать (конструировать) семейства родственных функций и классов.

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

В определении шаблона семейства функций используется служебное слово template . Для параметризации используется список формальных параметров шаблона, который заключается в угловые скобки <>. Каждый формальный параметр шаблона обозначается служебным словом class , за которым следует имя параметра (идентификатор). Пример определения шаблона функций, вычисляющих абсолютные значения числовых величин разных типов:

 

template<class type> type abs(type x){return x > 0? x: -x;}.

Описание шаблона семейства функций состоит из двух частей:

 

template<classтип_данных>тип_возвр_значенияимя_функции(список_параметров){тело_функции}.

В качестве еще одного примера рассмотрим шаблон семейства функций для обмена значений двух передаваемых им параметров.

 

template<class T> void swap(T *x, T *y){ T z = *x; *x = *y; *y = x;}.

Здесь параметр T шаблона функций используется не только в заголовке для спецификации формальных параметров, но и в теле определения функции, где он задает тип вспомогательной переменной z .

Шаблон семейства функций служит для автоматического формирования конкретных определений функций по тем вызовам, которые транслятор обнаруживает в теле программы. Например, если программист употребляет обращение abs(-10.3) , то на основе приведенного ранее шаблона компилятор сформирует такое определение функции:

 

double abs(double x){return x > 0? x: -x; }.

Далее будет организовано выполнение именно этой функции и в точку вызова в качестве результата вернется числовое значение 10.3.

Если в программе присутствует приведенный ранее шаблон семейства функций swap():

long k = 4, d = 8;swap(&k, &d);

то компилятор сформирует определение функции:

void swap(long *x, long *y){long x = *x; *x = *y; *y = x;}.

Затем будет выполнено обращение именно к этой функции и значения переменных k , d поменяются местами.

Если в той же программе присутствуют операторы:

 

double a = 2.44, b = 66.3;swap(&a, &b);

то сформируется и выполнится функция

voidswap(double *x, double *y){double x = *x; *x = *y;*y = x;}.

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

 

#include<iostream.h>//Функция определяет ссылку на элемент с максимальным значениемtemplate<class type> type &rmax(int n, type d[]){intim = 0;for (int i = 1; i < n; i++)im = d[im] > d[i]? im: i;return d[im];}int main(void){int n = 4;int x[] = { 10, 20, 30, 14 }; //Массив целых чиселcout<< "\nrmax(n,x) = " <<rmax(n, x); // rmax(n,x) = 30rmax(n, x) = 0;for (int i = 0; i < n; i++)cout<< "\tx[" << i << "] =" << x[i]; // x[0] = 10 x[1]...floatarx[] = { 10.3, 20.4, 10.5 }; //Массиввещественныхчиселcout<< "\nrmax(3,arx) = " <<rmax(3, arx); //rmax(3,arx) = 20.4rmax(3, arx) = 0;for (int i = 0; i < 3; i++)cout<< "\tarx[" << i << "] =" <<arx[i]; //arx[0] = 10.3...return 0;}.

В программе используются два разных обращения к функции rmax() . В одном случае параметр - целочисленный массив и возвращаемое значение – ссылка типа int . Во втором случае фактический параметр – имя массива типа float и возвращаемое значение имеет тип ссылки на float .

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

Параметры шаблонов

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

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

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

 

#include<iostream.h>int N = 0; //статическая, инициализирована нулемtemplate<classN>Nmax(Nx, Ny){ N a = x;cout<< "\nСчетчик обращений N = " << ++::N;if (a < y) a = y;return a;}int main(void){int a = 12, b = 42;max(a, b); //СчетчикобращенийN = 1floatz = 66.3, f = 222.4;max(z, f); //Счетчик обращений N = 2}.

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

 

template<class A, class B, class C>B func(A n, C m){ B value;...}.

В данном неверном примере остался неиспользованным параметр шаблона с именем B. Его применение в качестве типа возвращаемого функцией значения и для определения объекта value в теле функции недостаточно.

6. Определенная с помощью шаблона функция может иметь любое количество непараметризированых формальных параметров. Может бытьнепараметризировано и возвращаемое функцией значение. Например, в следующей программе шаблон определяет семейство функций, каждая из которых подсчитывает количество нулевых элементов одномерного массива параметризованного типа:

 

#include <iostream.h>template<class D> long count0(int, D *); //Прототипшаблонаint main(void){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) / sizeofX[];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 параметр T используется только в спецификации одного формального параметра array . Параметр size и возвращаемое функцией значение имеют явно заданные непараметризованные типы. Как и при работе с обычными функциями, для шаблонов функций существуют определения и описания. В качестве описания шаблона функций используется прототип шаблона:

 

template<список_параметров_шаблона>.

7. В списке параметров прототипа шаблона имена параметров не обязаны совпадать с именами тех же параметров в определении шаблона.

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

 

template<class E> void swap(E, E);

недопустимо использовать такое обращение к функции:

int n = 4;double d = 4.3;swap(n, d); // Ошибка в типах параметров.

Для правильного обращения к такой функции требуется явное приведение типа одного из параметров. Например, вызов:

 

swap(double(n), d); // Правильные типы параметров

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

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  




Подборка статей по вашей теме: