План-конспект уроку

Содержание

Тема: Шаблоны функций. Переопределение шаблонов функций

Результат работы программы

16-16d123881.441.44

В приведенной выше программе функция noName перегружена семь раз. Первые два определения отличаются только одним параметром int first и unsigned int first. Четвертое и пятое опеределения отличаются типом второго параметра (соответственно char и char*). Шестое и седьмое определения отличаются тем, что у одного параметр имеет тип float, а у другого — double.

ВОПРОС: Что, если из предпоследней строки описанной программы убрать операцию приведения типов (float)?

ОТВЕТ: Компилятор будет счетать, что константа 1.2 имеет тип double. Соотвественно, будет вызвана функция которая имеет следующий заголовок:

double noName(double r) //Седьмая функция c тем же именем

ВОПРОС: Что, если в описанную программу добавить следующее опеределение функции?

Float noName(int first){
return (float) first*first;}

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

Еще раз напомним, что компилятор выбирает функцию в соответствии с типами аргументов и их количеством. Правило, по которому осуществляется этот выбор, называется алгоритмом соответствия сигнатуре (в оригинале это пишется как signature matching algorithm).

Лекция № 12 (2часа)

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

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

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

int Abs(int N){return N < 0? -N: N;}double Abs(double N){return N < 0.? -N: N;}

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

template <typename T> T Abs (T N){ return N < 0? -N: N;}

В этом определении идентификатор T является параметром типа. Он определяет тип переменной или константы, передаваемой при вызове функции. Если программа вызывает функцию Abs и передает ей значения типа int, например,

cout << "Абсолютное значение для -5 = " << Abs(-5);

то компилятор автоматически создаст версию функции, в которой идентификатор T имеет тип int, и добавит в программу вызов данной версии функции.

Созданная компилятором функция будет эквивалентна функции, определенной явно:

int Abs (int N){ return N < 0? -N: N;}

Аналогично, если программа вызывает функцию Abs и передает ей значение типа double, например,

double D = -5.12;cout << "Абсолютное значение для D = " << Abs(D);

то компилятор автоматически сгенерирует версию функции, в которой заменит параметр типа T на double, и добавит в программу вызов данной версии функции. Эта версия функции эквивалентна следующей:

double Abs(double N){ return N < 0.? -N: N;}

Таким же образом компилятор создает дополнительные версии функции для каждого вызова, в котором указывается новый числовой тип данных, например, short или float. Генерация новой версии функции называется созданием экземпляра шаблона функции.

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

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

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

Еще один пример шаблона функции:

template <typename T> T Max (T A, T B){ return A > B? A: B;}

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

cout << "Большее из 10 и 5 = " << Max(10, 5) << endl;cout << "Большее из 'A' и 'B' = " << Max('A', 'B') << endl;cout << "Большее из 3.5 и 5.1 = " << Max(3.5, 5.1) << endl;

А следующий вызов является недопустимым:

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 - второго.

Для такой версии шаблона следующий оператор допустим и печатает значение 5.55

cout << "Большее из 10 и 5.55 = " << Max(10, 5.55);

В С++ параметр типа можно использовать в любом месте кода, в котором используется имя типа.

Так как возвращаемое значение преобразуется к типу второго параметра, то при изменении порядка параметров предыдущего примера

cout << "Большее из 5.55 и 10 = " << Max(5.55, 10);

результат сравнения будет округлен и равняться 5.

Каждый параметр типа, встречающийся внутри символов "<" и ">", должен также появляться в списке параметров функции. Т.е. следующее определение шаблона функции недопустимо:

template <typename T1, typename T2> T1 Max(T1 A, T1 B) { return A > B? A: B;}// ОШИБКА! список параметров должен включать T2 как параметр типа.

При таком определении компилятор, встретив вызов функции, не сможет определить значение идентификатора T2. Это - ошибка, даже если идентификатор T2 не использован.

Переопределение шаблонов функций

Каждая версия функции, генерируемая с помощью шаблона, содержит одинаковый базовый код. Единственным изменяемым свойством функции будут значение параметра (или параметров) типа. Однако для отдельного параметра (или параметров) типа можно обеспечить специальную обработку. Для этого определяется обычная функция языка С++ с тем же именем, что и шаблон функции, но использующая уже имеющиеся типы данных, а не параметры типов. Обычная функция переопределяет шаблон. Т.е., если компилятор обнаруживает, что типы переданных параметров соответствуют спецификации обычной функции, то он вызовет ее, а не создаст функцию по шаблону.

специальный символ «*». Это указатель количества. Запись B* означает любое количество слов типа B (слов, выводимых из символа B), либо пустое слово. Другими словами, символ «*» означает 0, 1, 2,... раз. Есть также специальные символы + и?:

* -- 0,1,..., раз.

+ -- 1,2,..., раз (то есть, любое число раз, но по крайней мере один раз).

? -- 0 или 1 раз (то есть либо есть, либо нет).

Інформатика


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



double arrow
Сейчас читают про: