Шаблоны функций в С++ для начинающих Первое знакомство

Начинающим программистам очень часто непонятны шаблоны. Шаблоны для новичков в большинстве случаев как тёмный лес. В некоторых книгах о шаблонах написано очень мудрёно, в некоторых попроще. Попытаемся понять, что такое шаблоны функций.
Для начала напишем код, в котором задействуем шаблонную функцию:

  • Шаблоны функций — они действуют подобно универсальной перегрузке функций.
Конечно, шаблоны функций не являются на 100% универсальным способом перегрузки, но основной принцип работы шаблонов очень сильно похож именно на универсальную перегрузку функций. Вместо того, чтобы писать много функций, описывая каждую для конкретного случая, можно написать одну функцию, для которой описать общий вид, и использовать для этой функции вызовы с явным указанием типов. Т. е. настоящие типы описывать не в описании функции, а в месте вызова функции. Это называется обобщённым программированием. Для того, чтобы это работало — используют шаблоны функций.
  • Ключевое слово template обозначает шаблон.
  • template <class T> обозначает Шаблон функции с одним параметром T.
  • Внутри угловых скобок шаблона функции описываются параметры шаблона.
При задействовании шаблона можно легко увидеть сходство с описанием прототипа функции, только скобки угловые и в качестве типов используются не типы, а ключевые слова. И описывается шаблон непосредственно перед функцией, а не где попало.
Ключевое слово "ШАБЛОН"
Ключевое слово "ПАРАМЕТР ШАБЛОНА"
Имя параметра

(любое)

template
class
T1

template
typename
T2

Имена параметров чаще всего называют Т1, Т2, Т3 и т. д., это из-за того, что шаблонный параметр — это тип: Тип1, Тип2, Тип3…
Угловые скобки — они как круглые скобки для функций: внутри них происходит описание параметров. Только угловые скобки дают точно понять, что параметры относятся к шаблонам. Как и у функций можно описывать разное число параметров, так и у шаблонов разное.
После написания template и указания в угловых скобках всех параметров (В приведенном примере только один параметр: T), написана функция MyFunc. Вместо явного указания типа функции и типов параметров этой функции — было задействовано обозначение типа: просто тип. Наша буква T — это, в нашем случае, просто любой тип. В этот тип можно заслать любой тип данных: int, float, char*, MyClass — вообще любой.
Сейчас самое время вам осознать тот факт, что шаблон функции помогает отвлечься от явного задания типов: типу, возвращаемому функцией, и типам параметров функций. Можно сказать, что шаблон функции вытеснил описание типов и встал на их место: вместо явного написания типов нами используются шаблонные обозначения типов.
Дальше проще.



Функция MyFunc возвращает некоторое значение. Для того, чтобы вам было максимально просто понять статью — в моём примере задействуется только один шаблонный параметр. Фактически при описании функции задействуются два типа: тип для возвращаемого функцией значения и тип для одного параметра функции, заявленного в круглых скобках функции. Но если эти два фактических типа одинаковые, то для них можно использовать одно обозначение, т. е. использовать одно имя шаблонному параметру. В противном случае, если бы типы различались, нужно бы было давать разные имена шаблонным параметрам, что на самом деле логично.
Снова возвращаемся к примеру. Нужно разобраться, что происходит внутри функции main.



Мы три раза обращаемся к функции MyFunc, и каждый раз в неё передаётся один аргумент (внутри круглых скобок при вызове функции), и значение этого аргумента принимается в формальный параметр функции (внутри круглых скобок самой функции). В ходе каждого из трёх вызовов получается так, что типы задействуются разные, мы подбираем значения этих типов и выводим на экран с помощью cout. Всё это отображает немного смысла использования шаблонов: без шаблона пришлось писать три функции, для каждой нужно было бы явно указывать типы. А мы вместо явного указания только-лишь обозначили, что будет тип: "а какой тип будет — не твоё дело, жалкая функция".


  • Перегруженные функции обычно используются для выполнения сходных действий, связанных с различной программной логикой для различных типов данных. Если программная логика и действия для всех типов идентичны, это можно выполнить более компактно и удобно, используя шаблоны функций.
Покажу ещё один пример использования шаблонов. Правда, сейчас не для функции. И я не видел, чтобы кто-то такое делал (не довелось мне увидеть), поэтому даже предположить не могу, зачем это может понадобиться.

Вроде бы нам говорили, что нельзя использовать одно имя переменной, а тут работает шаблонная магия: мы используем одно имя переменной для хранения значений разных типов. Всё, что нам нужно задействовать — это подставлять нужный тип во время обращения к переменной. Подставляем тип в угловые скобки в момент обращения к переменной — работает магия.
Глобальные переменные используют очень редко и в показываемом коде использована глобальная переменная. Ну, это стечение обстоятельств, что я её использую для написания своей этой статьи. В показанном коде значения для переменной задавались выборочно, из-за чего в последнем выводе оказывалось число 0, когда в предшествующих последнему выводу отображались значения, которые мы явно задали. Значение для незадействованного типа можно легко подменить на другое, единственное, нужно будет использовать похожие типы: типы должны будут уметь неявно приводиться один к другому.

Но вернёмся к функциям. Шаблоны — они и есть шаблоны. Шаблоны для функций работают по тому же принципу, по которому работают только что показанные вам примеры. Наша задача — обозначить, что у функции в каких-то местах будут какие-то типы, а когда будет нужно вызвать функцию, явно подставлять типы в угловые скобки.
Как ещё лучше расписать, я, если честно, не знаю. Да и код легче понять, чем моё подробное описание.
Смотрим и изучаем ещё один небольшой стандартный пример: сравнение двух чисел и вывод на экран большего.

Небольшой код, в функцию max передается два параметра, типы которых, при приеме внутри функции, определяются шаблоном. В самом шаблоне определяемый тип указан как один параметр. В коде закомментирована строчка, которая может привести к ошибке. Просто если в самом шаблоне указан один параметр, то такая запись интерпретируется компилятором как "Сколько бы функция не принимала параметров вовнутрь себя, возложив на меня обязанности указания типов, все эти параметры я расценю как однотипные" и, соответственно, работайте с ними как с однотипными и или не выпендривайтесь, или задавайте типы явно. На любом экзамене подобный ответ скорее всего двойка, но зато хорошо помогает понять некоторые принципы.
  • Сколько параметров задействовано у шаблонов, столько типов можно использовать при описании шаблонных параметров функций.
  • Параметры шаблонов пишутся в угловых скобках.
  • Параметры шаблонов разделяются запятыми.
  • Каждому параметру шаблона нужно приписать обозначение типа ключевым словом typename или class, обозначающим обобщенный тип.
  • Ключевые слова typename и class, с предварительным обозначением их ключевым словом template, семантически не различаются, однако многие программисты предпочитают для улучшения чтения кода давать обычным переменным обозначение ключевым словом typename, переводящимся как имя типа, а переменным-объектам классов давать обозначение ключевым словом class, переводящимся как класс.
Для задействования нескольких типов нужно только давать различные названия шаблонным параметрам:
Тип фунции
Имя функции
Тип параметра
Имя параметра
Тип параметра
Имя параметра

T1
foo1
(
T1
value1
,
T1
value2
)

T1
foo2
(
T2
value1
,
T3
value2
)

T
foo2
(
T1
value1
,
T
value2
)

Пишем функцию сравнения двух чисел, но в отличие от предыдущего примера, в настоящем будут задействованы разные типы:

Внимательный читатель может заметить, что в строчке:



на экран выводится не 55.555, а 55
Для функции указан тип T1, такой же тип указан для первого параметра функции, значит, функция будет возвращать значение с тем же типом, какой у первого параметра. Т1 он и есть Т1 — один и тот же тип. Первый аргумент, ушедший в функцию в той строчке имел тип int, поэтому в T1 будет подставлен тип int, для второго параметра указан тип T2, в который подставляется тип второго аргумента, ушедшего в функцию, т. е. Т2 после срабатываения нашей строчки становится double. После подстановки правильных типов в места обощений происходит сравнение значений двух разных типов (int сравнивается с double), а после сравнения какое бы значение не оказалось большим, оно приводится к типу T1, т. е. к типу, который отдаёт функция. Таким образом несмотря на то, что большим оказалось значение с типом double, оно усеклось до типа, отдаваемого функцией. Поэтому мы видим целое число.
С неявными преобразованиями типов в С++ можно познакомиться, почитав статью:

Если всё ещё плохо понятно, попробую объяснить немного иначе.

Срабатывает вот такая цепочка событий:

—> Из функции main вызывается функция max. В функцию max отдаётся 2 аргумента>
—> Функция max принимает два пришедших в неё аргумента в свои два параметра
—> В Функции max для обработки данных происходит некоторый анализ при помощи шаблона
—> В шаблоне указано, что функция имеет два различных по типам параметра (T1 и Т2), компилятор говорит: Понял, разберусь
—> Компилятор обработал данные с помощью шаблона и переиначил обозначения T1 и Т2 под типы пришедших в функцию аргументов
—> Код написан таким образом, что тип функции max тот же, что и тип первого принимаемого параметра
—> Поскольку возвращаемый функцией тип T1, в который пришёл int, отдачей будет целое число int

Кроме всего вышенаписанного могу ещё добавить, что шаблонные параметры можно использовать не только для указания типу функции и указанию типов параметров функции, но и для внутренних для функции переменных:

По факту — всё работает на одном принципе. Просто кому-то может понадобиться использовать шаблон таким образом, где внутри функции определяется какая-то локальная переменная, тип которой определяется с помощью шаблона, или возможны какие-то другие причины.
  • Шаблоны функции позволяют вам объявлять типонезависимые или общие функции.
  • Если вы используете функцией только один какой-то явно конкретный тип, нет особой необходимости применять шаблон.
  • Если параметрам функции требуется несколько различных типов, то в шаблоне надо назначить каждому типу уникальный идентификатор, благодаря которому компилятор в процессе компиляции обработает шаблонный параметр как переменную нужного типа.
Похожие темы:

7 комментариев на «“Шаблоны функций в С++ для начинающих Первое знакомство”»

  1. Вадим:

    Спасибо!

  2. Аноним:

    Мне понравилось изложение )

  3. Gen:

    Повторюсь, краткость сестра таланта.Очень доходчиво.Спасибо!

  4. Андрей:

    Браво! Сразу все ясно! Спасибо огромное!

  5. Анна:

    Спасибо за разъяснение:)))очень доходчиво!

  6. Аноним:

    По новому стандарту вместо class нужно использовать typename. class тоже поддерживается, но оставлен для совместимости с ранее написанными программами.

    Это просто к сведению. Вдруг, кому-то пригодится.

  7. Аноним:

    даже читать не хочется из-за скачущей рекламы

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/s-m-okulov/diskretnaya-matematika-teoriya-i-praktika-resheniya-zadach-po-informatike-uchebnoe-posobie/?lfrom=15589587
Яндекс.Метрика