Рассмотрим случай, когда компилятор не знает, как на основе вызова шаблонной функции вывести тип T. В данном коде я определил функцию Max от двух аргументов a и b. У меня здесь объявлен шаблонный параметр тип T, функция Max соответственно возвращает объект типа T, а внутри просто проверяет: если b < a, то она возвращает a, иначе — b. Попробуем ее вызвать. Вызовем ее от чисел 2 и 3. Соберем наш код и запустим его. Видим, что в консоль вывелось 3. Замечательно. Все работает так, как нам нужно. Теперь попробуем сравнить целое число и вещественное. Будем передавать в функцию не 3, а например, 3,5. Соберем наш код. Что мы видим? Во-первых, откроем ошибки более крупно и смотрим. В функцию переданы неправильные аргументы, а более детальная диагностика говорит, что вывод шаблонного параметра типа T не может состояться, потому что компилятор не знает что поставить: int или double. Как решаются такие ситуации? В таких ситуациях мы должны либо привести аргументы к одному типу, либо подсказать компилятору, какой тип используется вместо типа T. Делается это следующим образом. После имени функции мы открываем угловую скобку, пишем шаблонные параметры типы, которые мы хотим там использовать, и закрываем угловую скобку. В данном случае вместо типа T я хочу использовать тип double. Соответственно здесь я пишу double, закрываю угловую скобку, собираю код. Видим, что ошибки компиляции пропали. Попробуем запустить код. Видим, что на экран вывелось 3,5. Попробуем заменить тип double на тип int. Соберем наш код, он опять собрался. Запустим его и видим, что на экран вывелось 3. То есть аргумент вещественный 3,5 был приведен к целому типу типа int и стал равен 3. Соответственно, функция Max тоже возвращает int. Что еще здесь можно улучшить и поправить? Как мы знаем, нехорошо писать собственные функции, когда они уже должны быть в библиотеке. Вызовем стандартную функцию max из библиотеки algorithm. Она тоже шаблонная, и видим, что когда мы явно указываем int, она считает, что мы в нее передаем аргументы целого типа. Если мы укажем аргумент типа double, эта функция выведет 3,5. Она обработает оба аргумента как вещественные числа. А если мы не укажем никакого типа вообще, то у нас будет уже знакомая нам ошибка компиляции. В этом видео мы рассмотрели, что делать, когда компилятор затрудняется вывести шаблонный параметр тип на основе вызова шаблонной функции. В данном блоке видео мы рассмотрели шаблонные функции в языке C++. Вспомним основные моменты. Во-первых, чтобы объявить шаблонную функцию, надо написать ключевое слово template, дальше в угловых скобках указать шаблонные параметры типы, которые мы хотим использовать к нашей функции. А дальше стандартным образом мы определяем сигнатуру функции и ее тело. Замечу, что вместо слова typename можно писать слово class. Я предпочитаю слово typename, но в данном контексте они полностью эквивалентны. Шаблонный тип. Что о нем надо знать? Во-первых, он может быть автоматом выведен на основе контекста вызова функции, а также после того как мы его объявили в нашей шаблонной функции, мы можем его использовать, как любой другой стандартный тип в языке C++. Например, в данной функции я написал template <class T>, после этого я объявляю функцию Foo, которая возвращает объект типа T, принимает на вход переменную типа T, а также внутри своего тела объявляет переменную var2, которая тоже имеет тип T. То есть работа с типом T, после того как мы его объявили как шаблонный параметр тип, она ничем не отличается от работы с другими классами в языке C++. Про выведение шаблонного типа. В данном примере я вызываю функцию max от аргументов 2 и 3. Компилятор понимает, что это два целых числа, и тип для функции max шаблонной будет выведен как int. В случае, когда компилятор не может вывести тип, например, когда мы вызываем функцию max от целого и вещественного числа 2 и 3,5, мы должны ему подсказать, какой тип использовать. Это делается с помощью угловых скобок после имени функции и до скобок с аргументами. Итак, подытожим, для чего же нужны шаблоны в языке C++. Во-первых, они позволяет программировать по контракту, то есть мы в своих шаблонных функциях не указываем явно типы, с которыми мы хотим работать. Мы объявляем шаблонный параметр тип и накладываем на них некоторые ограничения, некий контракт, что, например, шаблонный тип должен поддерживать умножение на точно такой же тип или быть сравнимым с другим шаблонным типом. Также шаблоны позволяют снижать связность кода. Ведь чем меньше мы знаем, как устроен мир вокруг нашей шаблонной функции, тем более гибкий и абстрактный код мы можем писать, а также шаблоны позволяют уменьшить дублирование кода. Вспомним пример, когда мы для стандартных контейнеров библиотеки STL написали операторы вывода в поток в красивом формате, что было изначально? Изначально у нас было много дублирующихся функций. После этого с помощью шаблонов мы ввели функцию join sec, объявили операторы вывода для конкретных контейнеров, которые в себе не содержали практически никакого дублирования, и в итоге все это у нас заработало изящно и красиво.