C++. Бархатный путь. Часть 2 - Шаблоны функций и шаблонные функции

ОГЛАВЛЕНИЕ

 

Шаблоны функций и шаблонные функции

Рассмотрим простую функцию, реализующую алгоритм сравнения двух величин:

int min (int iVal_1, int iVal_2)
{
return iVal_1 < iVal_2 ? iVal_1 : iVal_2;
/*
Возвращается значение iVal_1, если это значение меньше iVal_2.
В противном случае возвращается значение iVal_2.
*/
}

Для каждого типа сравниваемых величин должен быть определён собственный вариант функции min(). Вот как эта функция выглядит для float:

float min (float fVal_1, float fVal_2)
{
return fVal_1 < fVal_2 ? fVal_1 : fVal_2;
}

А для double… А для…

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

#define min(a,b)   ((a)<(b)?(a):(b))

Это определение правильно работает в простых случаях:

min(10, 20);
min(10.0, 20.0);

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

C++ предоставляет ещё одно средство для решения этой задачи. При этом сохраняется присущая макроопределениям краткость и строгость контроля типов языка. Этим средством является шаблон функции.

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

Синтаксис объявления шаблона определяется следующим множеством предложений Бэкуса-Наура:

Объявление ::= ОбъявлениеШаблона
ОбъявлениеШаблона ::= template <СписокПараметровШаблона> Объявление
СписокПараметровШаблона ::= ПараметрШаблона
::= СписокПараметровШаблона, ПараметрШаблона
ПараметрШаблона ::= ТиповыйПараметр
::= *****
ТиповыйПараметр ::= class Идентификатор

Итак, объявление и определение шаблона функции начинается ключевым словом template, за которым следует заключённый в угловые скобки и разделённый запятыми непустой список параметров шаблона. Эта часть объявления или определения обычно называется заголовком шаблона.

Каждый параметр шаблона состоит из служебного слова class, за которым следует идентификатор. В контексте объявления шаблона функции служебное слово class не несёт никакой особой смысловой нагрузки. Дело в том, что аналогичная конструкция используется также и для объявления шаблона класса, где, как скоро увидим, ключевое слово class играет свою особую роль. В заголовке шаблона имена параметров шаблона должны быть уникальны.

Следом за заголовком шаблона располагается прототип или определение функции - всё зависит от контекста программы. Как известно, у прототипа и определения функции также имеется собственный заголовок. Этот заголовок состоит из спецификатора возвращаемого значения (вполне возможно, что спецификатором возвращаемого значения может оказаться идентификатор из списка параметров шаблона), имя функции и список параметров. Все до одного идентификаторы из заголовка шаблона обязаны входить в список параметров функции. В этом списке они играют роль спецификаторов типа. Объявления параметров, у которых в качестве спецификатора типа используется идентификатор из списка параметров шаблона, называется шаблонным параметром. Наряду с шаблонными параметрами в список параметров функции могут также входить параметры основных и производных типов.

Шаблон функции служит инструкцией для транслятора. По этой инструкции транслятор может самостоятельно построить определение новой функции.

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

В качестве примера рассмотрим программу, в которой для определения минимального значения используется шаблон функции min().

template <class Type> Type min (Type a, Type b);
/*
Прототип шаблона функции.
Ключевое слово template обозначает начало списка параметров
шаблона. Этот список содержит единственный идентификатор Type.
Сама функция содержит два объявления шаблонных параметра,
специфицированных шаблоном параметра Type.
Спецификация возвращаемого значения также представлена шаблоном
параметра Type.
*/
int main (void)
{
min(10,20);// int min (int, int);
min(10.0,20.0);// float min (float, float);
/*
Вызовы шаблонной функции. Тип значений параметров определён.
На основе выражения вызова (транслятор должен распознать тип
параметров) и определения шаблона транслятор самостоятельно
строит различные определения шаблонных функций. И только после
этого обеспечивает передачу управления новорождённой шаблонной
функции.
*/
return 1;
}
template <class Type>
Type min (Type a, Type b)
{
return a < b ? a : b;
}
/*
По аналогии с определением функции, эту конструкцию будем называть
определением шаблона функции.
*/

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