Шаблоны функций.
Шаблоны функций в языке С позволяют создать общее определение функции,
применяемой для различных типов данных.
В прошлой теме, чтобы использовать одну и ту же функцию с различными типами
данных мы создавали отдельную перегруженную версию этой функции для каждого
типа. Например:
int Abs(int N)
{
return N < 0 ? -N : N;
}
double Abs(double N)
{
return N < 0. ? -N : N;
}
|
Теперь, используя шаблон мы сможем реализовать единственное
описание, обрабатывающее значения любого типа:
template <typename T> T Abs (T N)
{
return N < 0 ? -N : N;
}
|
Теперь - обсудим то, что у нас получилось.
1. Идентификатор T является параметром типа. Именно он
определяет тип параметра, передаваемого в момент вызова функции.
2. Допустим, программа вызывает функцию Abs и передает ей значения
типа int:
cout << "Result - 5 = " << Abs(-5);
|
3. В данном случае, компилятор автоматически создает версию функции, где
вместо T подставляется тип int.
4. Теперь функция будет выглядеть так:
int Abs (int N)
{
return N < 0 ? -N : N;
}
|
5. Следует отметить, что компилятор создаст версии функции для любого вызова,
с любым типом данных. Такой процесс носит название - создание
экземпляра шаблона функции.
Основные принципы и понятия при работе с шаблоном.
Теперь, после поверхностного знакомства - мы рассмотрим все особенности
работы шаблонов:
1. При определении шаблона используются два спецификатора: template и
typename.
2. На место параметра типа Т можно подставить любое корректное
имя.
3. В угловые скобки можно записывать больше одного параметра типа.
4. Параметр функции - это значение, передаваемое в функцию при выполнении
программы.
5. Параметр типа - указывает тип аргумента, передаваемого в функцию, и
обрабатывается только при компиляции.
Процесс компиляции шаблона.
1. Определение шаблона не вызывает генерацию кода компилятором
самостоятельно. Последний создает код функции только в момент её вызова и
генерирует при этом соответствующую версию функции.
2. Следующий вызов с теми же типами данных параметров не спровоцирует
генерацию дополнительной копии функции, а вызовет ее уже существующую копию.
3. Компилятор создает новую версию функции, только если тип переданного
параметра не совпадает ни с одним из предыдущих вызовов.
Пример работы с шаблоном.
template <typename T> T Max (T A, T B)
{
return A > B ? A : B;
}
|
1. Шаблон генерирует множество функций, возвращающих большее из двух значений
с одинаковым типом данных.
2. Оба параметра определены как параметры типа T и при вызове функции
передаваемые переметры должны быть строго одного типа. В данном случае возможны
такие вызовы функции:
cout << "Большее из 10 и 5 = " << Max(10, 5) << "\n";
cout << "Большее из 'A' и 'B' = " << Max('A', 'B') << "\n";
cout << "Большее из 3.5 и 5.1 = " << Max(3.5, 5.1) << "\n";
|
А такой вызов приведет к ошибке:
cout << "Большее из 10 и 5.55 = " << Max(10, 5.55); // ОШИБКА!
|
Компилятор не сможет преобразовать параметр int в double.
Решением проблемы передачи разных параметров является такой шаблон:
template <typename T1, typename T2> T2 Max(T1 A , T2 B)
{
return A > B ? A : B;
}
|
В этом случае Т1 обозначает тип значения, передаваемого в качестве
первого параметра, а Т2 - второго.
ВНИМАНИЕ!!!
Каждый параметр типа, встречающийся внутри угловых скобок, должен ОБЯЗАТЕЛЬНО
появляться в списке параметров функции. В противном случае произойдет ошибка на
этапе компиляции.
template <typename T1, typename T2> T1 Max(T1 A , T1 B)
{
return A > B ? A : B;
}
// ОШИБКА! список параметров должен включать T2 как параметр типа.
|
Переопределение шаблонов функций
1. Каждая версия функции, генерируемая с помощью шаблона, содержит один и тот
же фрагмент кода.
2. Однако, для отдельных параметров типа можно обеспечить особую реализацию
кода, т. е. определить обычную функцию с тем же именем, что и шаблон.
3. Обычная функция переопределит шаблон. Если компилятор находит типы
переданных параметров соответствующие спецификации обычной функции, то он
вызовает ее, и не создает функцию по шаблону.