Шаблон функций
Большинство функций имеют одно и то же тело кода, независимо от типа. Например, инициализация содержимого одного массива от другого того же самого типа. Обычно это код выглядит так:
for(i=0; i<n; i++) a[i]=b[i];
Данный код можно на С автоматизировать простой макрокомандой.
#define COPY(A,B,N) { int i; for(i=0; i<N; i++) A[i]=B[i]; }
Она работает, но не безопасно по отношению к типу. Пользователь легко смешивает типы при несоответствующих преобразованиях. Для достижения подобных эффектов на С++ можно использовать различные формы преобразования и перегрузки. Однако, при отсутствии соответствующих преобразований и сигнатур, не будет предприниматься никаких действий. Шаблоны обеспечивают для этого следующий полиморфный языковый механизм:
template <class TYPE>
void copy(TYPE a[],TYPE b[],int n) {
for(int i=0; i<n; i++) a[i]=b[i];
}
Вызов copy() со специфическими параметрами заставляет компилятор на основании этих параметров, генерировать действительную функцию. Если это невозможно, то возникает ошибка во время компиляции.
double f1[50], f2[50];
copy(f1,f2,50);
char c1[25],c2[50];
copy(c1,c2,10);
int i1[75],i2[75];
copy(i1,i2,40);
char *ptr1, *ptr2;
copy(ptr1,ptr2,100);
copy(i1,f2,50); // error
copy(ptr1,f2,50); // error
Последние два вызова приведут к ошибке компиляции. Типы фактических параметров не соответствую шаблону. Запись следующего вида не вызовет ошибку
copy(i1,(int *)f2,50);
Однако при этом будет получена несоответствующая форма копирования. Дело в том, что родовая процедура копирования должна получать в виде параметров два класса отличающегося типа.
template class T1, class T2>
void copy(T1 a[], T2 b[], int n) { for(int i=0; i<n; i++) a[i]=b[i]; }
В этой форме существует поэлементное преобразование. Оно обычно соответствует и наиболее безопасному преобразованию.
Совпадение сигнатуры и перегрузка
Часто родовая подпрограмма не может работать для специального случая. Следующая форма шаблона обмена (swapping) работает для базовых типов.
template <class T>
void swap(T &x, T& y) { T temp; temp=x; x=y; y=temp; }
Шаблон функции используется, чтобы создавать для любого вызова соответствующую функцию, недвусмысленно соответствующую параметрам.
int i.j;
char str1[100],str2[100];
complex c1,c2;
swap(i,j); // i, j -int допустимо
swap(с1,с2); // с1, с2 -complex допустимо
swap(str1[50],str2[33]); // обе -char допустимо
swap(str1,str2); // допустимо
swap(i, chj); // i, -int, ch -char не допустимо
Сделаем так, чтобы swap работала для строк, представляемых как символьные массивы; для этого опишем следующий специальный случай.
void swap (char *s1, char *s2) {
int len=(strlen(s1) >= strlen(s2)) ? strlen(s1): strlen(s2);
char *temp=new char [len+1]; strcpy(temp,s1); strcpy(s1,s2); strcpy(s2,temp); delete temp;
}
С добавлением такого явного случая, при вызове с точным соответствием сигнатуре swap(), имеется преимущество над точным соответствием, найденным с помощью подстановки шаблона.
Алгоритм выбора функции перегрузки следующий:
-
Найти точное соответствие функции не-шаблону.
-
Найти точное соответствие, использующее шаблон функции
-
Обеспечить обычное разрешение параметра для функций не-шаблонов
Шаблоны классов
Дружественность
Шаблоны классов могут содержать друзей. Friend функция, не использующая спецификацию шаблона, будет универсальной friend для всех экземпляров шаблона класса. Friend функция, которая включает шаблоны параметров - особо friend только для того класса, экземпляр которого создается.
template <class T>
class matrix {
friend void foo_bar(); // универсальная
friend vect <T> product(vect <T> v); // создается экземпляр
};
Статические члены
Статические члены не универсальны, а специфичны для каждой реализации.
template <class T>
class foo {
public:
static int count;
};
template <class T> int foo::count=0;
....
foo <int> a;
foo <double> b;
Определены статические переменные foo<int>::count и foo<double>::cout.
Аргументы шаблона класса
Как классы, так и функции могут иметь несколько аргументов шаблона класса. Напишем функцию, которая преобразует значение одного типа к другому типу, при условии, что первый тип, по крайней мере, такой же длины как и второй.
template <class T1,class T2 >
boolean coerce(T1& x, T2 y) {
if(sizeof(x) >= sizeof(y)) x=(T1)y;
else false;
return true;
}
В этом шаблоне функции есть два, возможно различных, типа, описанных как параметры шаблона.
Другие параметры шаблона включают константные выражения, имена функций и символьные строки.
template <int n, class T>
class array {
public:
T a[n];
};
array <50,double> x,y;
x=y; // должно работать эффективно
Выгоды от параметризации состоят в распределении из системного стека, в противоположность распределению из свободной памяти. На большинстве систем это более эффективный режим. Тип привязывается к специфической целой константе так, что операции, использующие внутри себя массивы совместимой длины, безопасны по отношению к типу и проверены во время компиляции.