[БЕЗ_ЗВУКА] В данной лекции мы рассмотрим шаблоны функции в языке C++, посмотрим, как их создавать и для чего они применяются. Начнем с решения простой задачи — возведения числа в квадрат, допустим, целого. Напишем для этого специальную функцию: int Sqr, на вход она будет принимать x и возвращать x, умноженный на x. Выведем результат применения этой функции в поток. Соберем наш код и видим, что получилось 4 — все правильно. Теперь наша программа развивается, и мы хотим возводить в квадрат дробные числа, допустим 2.5. Запустим наш код. Видим, что опять получилось 4. Это неправильно. Почему? Потому что у нас нету функции, которая возводит в квадрат вещественные числа — числа типа double. Что мы делаем? Мы ее определяем, заменяем в ней тип аргумента и снова запускаем наш код. Видим, что получилось 6.25. Правильно! Это, конечно, хорошо, что мы решили свою задачу, но мы задублировали наш код — у нас появилось две одинаковые функции, которые делают одну и ту же вещь. Они умножают аргумент на самого себя и возвращают результат — значит, умножают x на x. Что бы мы хотели, чтобы избежать такого дублирования? На самом деле, нам было бы гораздо удобней написать функцию, которая принимала бы некий абстрактный тип, который поддерживал бы умножение элементов этого типа друг на друга и возвращал результат их умножения. То есть давайте попробуем написать такую сигнатуру функции. Мы бы хотели, чтобы наша функция возвращала некий тип T, пусть бы она называлась точно так же — Sqr. На вход бы она принимала элемент типа T и возвращала x * x. В данной функции нам нужно только то, чтобы элемент x поддерживал операцию умножения на другой элемент x, на самом деле на тот же самый. Значит, фактически мы только что написали шаблон функции в языке C++. Чтобы его завершить, осталось написать ключевое слово template и объявить этот шаблонный тип T. Для этого есть ключевое слово typename T. Теперь мы объявили шаблон функции. Давайте попробуем стереть все, что мы написали раньше. Наша шаблонная функция называется Sqr, вот мы ее вызываем. Соберем наш код. Видим, что появилось 6.25. Давайте попробуем возвести тройку. Видим, что получилось 9. То есть теперь у нас есть одна функция, она принимает аргумент типа T. Тип T компилятор выведет сам, единственное, что нам потребовалось сделать — это явно его объявить вот этим ключевым словом typename T для функции. Компилятор выведет его сам. И надо только, чтобы элементы типа T поддерживали операцию умножения друг на друга. В данном случае тип int и тип double поддерживают данную операцию, поэтому она работает. Попробуем возвести в квадрат пару. [ШУМ] Подключу модуль utility. И создадим пару из элементов 2 и 3. Возведем ее в квадрат. Видим, что появилось много ошибок компиляции, одна из которых гласит, что для оператора * не определены аргументы парой и парой. Что же, давайте мы это сделаем. Пишем шаблонный оператор умножения для пар. Во-первых, у нас там будет два шаблонных типа: First и Second — потому что пара состоит из двух элементов. Объявим первый, объявим второй. Мы будем возвращать пару, у которой элемент первый имеет тип First, а элемент второй имеет тип Second. Далее пишем ключевое слово operator *, и на вход мы будем принимать две пары: p1, p2. Далее. Шаблонные типы, шаблонные функции мы можем использовать по прямому назначению, например создавать переменные данного типа. Воспользуемся этой возможностью. Объявим элемент типа First и положим в него результат умножения первого элемента первой пары на первый элемент второй пары. [ШУМ] Аналогично поступим со вторым элементом в каждой паре. [ШУМ] И вернем из функции новую пару. После этого поправим наш оператор вывода. [ШУМ] [ШУМ] [ШУМ] И видим, что код успешно отработал. Мы видим 4 и 9. Это ровно 2² и 3². Попробуем вместо двух написать 2.5, то есть первый аргумент, первый элемент пары стал вещественным числом типа double. Запустим наш код и видим, что получилось 6.25. При этом — важный момент — нам не пришлось определять новых функций для умножения пар. Давайте посмотрим еще раз, как работает наш код. У нас есть шаблонная функция Sqr, она на вход принимает аргумент типа T. Тип T выведет сам компилятор. И возвращает элемент типа T, умноженный на элемент типа T. То есть требует, чтоб он поддерживал операцию умножения. Далее. Мы попробовали вызвать функцию для пары. Для пары по умолчанию операция умножения не определена, поэтому у нас возникла ошибка компиляции. И мы определили шаблоны оператора умножения для пары любого типа, то есть ее элементы, первый и второй — они могут быть абсолютно любого типа. Вот он определен, я объявил первый тип, второй тип и передал в оператор умножения пару с элементом первого типа First и с элементом второго типа Second — компилятор выводит их сам, когда вызывает. После этого вернул попарное перемножение в виде новой пары и вывел на консоль. Значит, какое преимущество нам дает использование шаблонов функций в языке C++? Мы очень сильно избавляемся от дублирования, ведь когда мы изменили аргумент в паре с целого типа на дробный, нам не пришлось писать никакого нового кода — все работало из коробки. В следующих лекциях мы рассмотрим, как еще применяются шаблоны функций в языке C++.