И.А. Волкова, А.В. Иванов, Л.Е. Карпов - Основы объектно-ориентированного программирования. Язык программирования С++ (ЧБ) (1114895), страница 16
Текст из файла (страница 16)
Так, еслинеобходимо написать функции нахождения суммы элементов числовых массивовразных типов (например, int, float или double), то вместо трех различныхфункций можно написать один шаблон.15.1. Параметры шаблонаДля описания шаблонов используется ключевое слово template, вслед закоторым указываются аргументы (формальные параметры шаблона), заключенные в угловые скобки. Формальные параметры шаблона перечисляются череззапятую, и могут быть как именами объектов, так и параметрическими именамитипов (встроенных или пользовательских). Параметр-тип описывается с помощьюслужебного слова class или служебного слова typename.89В соответствии со Стандартом ISO 1998 C++ (см.
[2], раздел 14.1.4) параметром шаблона может быть:− параметрическое имя типа− параметр-шаблон− параметр одного из следующих типов:9 интегральный9 перечислимый,9 указатель на объект любого типа (встроенного или пользовательского) или на функцию9 ссылка на объект любого типа (встроенного или пользовательского) или на функцию9 указатель на член класса, в частности, указатель на метод классаИнтегральные типы (раздел 3.9.1 Стандарта):− знаковые и беззнаковые целые типы,− bool, char, wchar_tПримечание. Перечислимые типы не относятся к интегральным, но их значенияприводятся к ним в результате целочисленного повышения.При использовании типа в качестве параметра перед параметром, являющимся параметрическим именем типа, необходимо использовать одно из ключевых слов: либо class, либо typename.Примечание. Не все компиляторы обрабатывают вложенные шаблоны (например,Microsoft* Visual C++ 6*: Visual Studio 98*).
Тем не менее, шаблонный класс можетиспользоваться в качестве значения параметра по умолчанию. Далее будет рассмотреншаблонный класс со следующим прототипом:template <class T , class A=allocator <T> > сlass vector{ . . .};В разделе 14.1.7 Стандарта явно сказано, что нетиповый параметр не можетиметь тип void, пользовательский тип, или плавающий тип, например:// template <double d> class X{ . .
. };template <double* d> class X{ . . . };template <double& d> class X{ . . . };// ошибка// OK// OKТаким образом, параметром шаблона не может быть формальный параметрнецелочисленного типа, например, float или double. Это можно объяснитьтем, что основным назначением формальных параметров в качестве параметровшаблона является определение числовых характеристик параметрических типов(например, размер вектора, стека и так далее).Вместе с тем параметром шаблона может быть указатель, в том числе и наобъект нецелочисленного типа. При этом, естественно, указатель – фактическийпараметр может содержать как адрес одного объекта, так и массива однородныхобъектов.90Использование шаблонов позволяет создавать универсальные алгоритмы,без привязки к конкретным типам.15.2.templateШаблоны функций.Список_параметров Тип_возвра щаемогоИмяформальные(параметры ){.
.};функциизначенияшаблонаПример:template <class T> T sum(T array[], int size){T res = 0;int i;for (i = 0; i < size; i++ )res += array[i];return res;}При обращении к функции-шаблону после имени функции в угловых скобкахуказываются фактические параметры шаблона - имена реальных типов или значения объектов:ИмяфункцииСписок_фактич.фактические(параметры );параметров _ шаблонаДля конкретного использования приведенного выше шаблона для массивовцелых чисел размерности 10 следует написать:int iarray[10];int i_sum;//...i_sum = sum <int> (iarray, 10);Встретив такую конструкцию, компилятор сгенерирует конкретную реализацию функции sum с учётом специфицированных параметров (такая функцияназывается порожденной функцией).Так как допускается параметризовать шаблоны не только именами типов, нои объектами, то аргумент size можно указать в виде параметра шаблона:template <class T, int size> T sum (T array[]) { /* ...
*/ }Тогда вызов sum будет выглядеть соответственно:i_sum = sum <int, 10> (iarray);Замечание. Следует отметить, что использование шаблонов сокращает текстпрограммы, но не сокращает программный код. В программе реально будет сгенерировано столько порожденных функций, сколько имеется вызовов функций сразными наборами фактических параметров шаблона.9115.3. Специализация шаблонной функцииПо сути, при использовании функции-шаблона используется механизм, позволяющий автоматически перегружать функции компилятором. Однако функцию-шаблон можно перегрузить и явно.Если шаблонный алгоритм является неудовлетворительным для конкретноготипа аргументов или неприменим к ним, то можно описать обычную функцию,список типов аргументов и возвращаемого значения которой соответствуют объявлению шаблона. Такая, перегружающая шаблон, функция, называется специализацией шаблонной функции.Следует отметить, что при обращении к функции-шаблону после именифункции можно и не указывать в угловых скобках фактические параметрическиетипы шаблона.
В этом случае компилятор автоматически определяет фактическиепараметрические типы шаблона по типам фактических параметров вызова функции. Это называется выведением типов аргументов шаблона. Выведение типоваргументов возможно при условии, что список фактических параметров вызовафункции однозначно идентифицирует список параметров шаблона.Так, описанную выше шаблонную функцию можно вызвать следующим образом:int iarray[10];int i_sum;//...i_sum = sum (iarray, 10);Так как тип первого фактического параметра – int, то компилятор автоматически трактует данный вызов, какi_sum = sum <int> (iarray, 10);Если параметр шаблона можно вывести из более, чем одного фактическогопараметра функции, результатом каждого выведения должен быть один и тот жетип.
Иначе вызов будет ошибочным.Пример:template <class T> void f(T i, T* p){ /* . . . */ }// . . .void g(int i){// . . .f(i, &i); // правильный вызов// . . .// f(i,”hello world”); - ошибка, т.к. по первому// параметру T – int, по второму – const char}Версия шаблона для конкретного набора фактических параметров шаблонатакже называется специализацией.При выведении типов аргументов шаблона по типам фактических параметров функции нельзя применять никаких описанных выше преобразованийпараметров, кроме преобразований Точного отождествления, то есть:92• Точное совпадение• Совпадение с точностью до typedef• Тривиальные преобразования:T[] <-> T*T <-> T&T -> const TПример:template <class T> T max(T t1, T t2){. . .}int main(){max(1,2);// max<int>(1,2);max(’a’,’b’);// max<char>(’a’,’b’);max(2.7, 4.9);// max<double>(2.7, 4.9);//max(’a’,1);// ошибка – неоднозначность,// стандартные преобразования не// допускаются//max(2.5,4);// ошибка – неоднозначность,// стандартные преобразования не// допускаются}Неоднозначности не возникают при использовании явного квалификатора:max <int>(‘a’, 1);max <double>(2.5, 4);15.4.Алгоритм поиска оптимально отождествляемой функции (с учетом шаблонов)С одним и тем же именем функции можно написать несколько шаблонов иперегруженных обычных функций.
При этом одна специализация считается болееспециализированной, чем другая, если каждый список аргументов, соответствующий образцу первой специализации, также соответствует и второй специализации.Алгоритм выбора перегруженной функции с учетом шаблонов являетсяобобщением правил выбора перегруженной функции.1. Для каждого шаблона, подходящего по набору формальных параметров,осуществляется формирование специализации, соответствующей спискуфактических параметров.2. Если могут быть два шаблона функции и один из них более специализирован, то на следующих этапах рассматривается только он (порядокспециализаций описан далее).3.
Осуществляется поиск оптимально отождествляемой функции из полученного набора функций, включая определения обычных функций,подходящие по количеству параметров. При этом если параметры некоторого шаблона функции были определены путем выведения по типамфактических параметров вызова функции, то при дальнейшем поиске93оптимально отождествляемой функции к параметрам данной специализации шаблона нельзя применять никаких описанных выше преобразований, кроме преобразований Точного отождествления4.
Если обычная функция и специализация подходят одинаково хорошо, товыбирается обычная функция.5. Так же, как и при поиске оптимально отождествляемой функции дляобычных функций, если полученное множество подходящих вариантовсостоит из одной функции, то вызов разрешим. Если множество пустоили содержит более одной функции, то генерируется сообщение обошибке.15.5. Шаблонные классыТак же, как и для функций, можно описать шаблоны для классов. Механизмшаблонов при описании класса позволяет, например, обобщенно описыватьмножество классов, единственное отличие которых заключается в используемыхтипах данных.Объявление шаблона класса:templateСписок_параметровшаблонаИмяclass класса {.
. .};Процесс генерации объявления класса по шаблону класса и фактическимаргументам шаблона называется инстанцированием шаблона. Обычно он совмещается с объявлением объекта соответствующего конкретного типа. Синтаксис такого объявления:ИмяклассаСписок_параметровшаблонаИдентификатор ;объектаФункции-члены класса-шаблона автоматически становятся функциями-шаблонами. Для них не обязательно явно задавать ключевое слово template.Пример: описание стека для хранения величин разных типов данных:template <class T> class stack{T* body;int size;int top;public:stack(int sz = 10){size = sz;top = 0;body = new T[size];}~stack(){delete[] body;}T pop(){top--;return body[top];94}void push(T x){body[top] = x;top++;}};int main(){stack<int>S1( 20);stack<char>S2(256);stack<double> S3( 16);// .
. .}Так же, как и в шаблонах функций, использование шаблонов классов сокращает текст алгоритма, но не сокращает размер кода программы. Реально генерируется столько описаний классов, сколько было объявлений объектов с разными параметрами шаблонов.Шаблонные классы могут иметь дружественные функции и классы. Дружественная функция, которая не использует параметры шаблона, имеется в единственном экземпляре, то есть она является дружественной для всех инстанцирований класса.Дружественная функция, которая использует параметры шаблона, сама является шаблоном. Конкретная реализация такой функции с учётом специфицированных параметров (порожденная функция) является дружественной для такого инстанцирования класса, которое совпадает по типам с фактическими типамиаргументов порожденной функции.Пример:template <class T> class Y{ .