Урок №173. Шаблоны функций

  Юрий Ворон  | 

    | 

  Обновлено 6 Ноя 2018  | 

 385

В предыдущих главах мы рассматривали как с помощью функций и классов сделать программы более удобными, безопасными и производительными. Хотя функции и классы являются мощными и гибкими инструментами для эффективного программирования, в некоторых случаях они ограничены из-за требования C++ указывать типы всех используемых параметров.

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

Всё отлично до тех пор, пока мы работаем с целочисленными значениями. А что если нам придется работать и со значениями типа double? Вы, вероятно, скажете перегрузить функцию max() для работы с типом double:

Теперь у нас есть две версии одной функции, которые работают с типами char, int, double и, если мы перегрузим оператор >, даже с классами! Однако, поскольку C++ требует, чтобы мы указывали типы наших переменных, нам приходится записывать несколько версий одной и той же функции, где единственное, что меняется — тип параметров.

А это, в свою очередь, головная боль для программистов, так как поддерживать такой код трудно и уходит уйма времени, и, что самое важное, это нарушает одну из концепций эффективного программирования – максимально минимизировать дублирующий код. Правда, было бы неплохо написать одну версию функции max(), которая работала бы с параметрами ЛЮБОГО типа?

Добро пожаловать в мир шаблонов.

Что такое шаблон функции?

Если посмотреть определение слова «шаблон» в словаре, то увидим следующее: «Шаблон — это образец, по которому изготавливаются похожие изделия». Например, шаблоном является трафарет – объект (например, пластинка), в котором прорезан рисунок (узор/символ). Если приложить трафарет к другому объекту и распылить краску, то получим этот же рисунок, прилагая минимум усилий, быстро и, что не менее важно, мы сможем сделать десятки этих рисунков разных цветов! При этом нам нужен лишь один трафарет и нам не нужно определять цвет рисунка заранее (до использования трафарета).

В C++ шаблоны функцийэто функции, которые служат образцом для создания других подобных функций. Главная идея – создание функций без указания точного типа(ов) некоторых или всех переменных. Вместо этого мы определяем функцию, указывая тип параметра шаблона, который используется вместо любого типа данных. После того, как мы создали функцию с типом параметра шаблона, мы фактически создали «трафарет функции».

При вызове шаблона функции, компилятор использует «трафарет» в качестве образца функции, заменяя тип параметра шаблона на фактический тип переменных, передаваемых в функцию! Таким образом, мы можем создать 50 «оттенков» функции, используя всего лишь один шаблон! Об этом детальнее мы поговорим в следующем уроке.

Создание шаблонов функций в C++

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

Рассмотрим еще раз целочисленную версию функции max():

Здесь мы трижды указываем тип данных: в параметрах a, b и в типе возврата функции. Для создания шаблона этой функции нам нужно заменить тип int на тип параметра шаблона функции. Поскольку в этом случае используется только один тип данных (int), то нам нужно указать только один тип параметра шаблона.



Мы можем назвать этот тип как-угодно, главное, чтобы это не было зарезервированным/ключевым словом. В C++ принято называть типы параметров шаблонов большой буквой T (сокращение от «Type»).

Вот наша переделанная функция max():

Но это еще не всё. Программа работать не будет, так как компилятор не знает, что такое «Т»!

Чтобы всё заработало, нам нужно сообщить компилятору две вещи:

   Определение шаблона функции.

   То, что T является типом параметра шаблона функции.

Мы можем сделать это в одной строке кода, выполнив объявление шаблона (а вернее, объявление параметров шаблона):

Эврика! Работает!

Рассмотрим детальнее объявление параметров шаблона:

   Сначала пишем ключевое слово template, которое сообщает компилятору, что дальше мы будем объявлять параметры шаблона.

   Параметры шаблона функции указываются в угловых скобках (<>).

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

   Затем называем тип параметра шаблона (обычно «T»).

Если требуется несколько типов параметров шаблона, то они разделяются запятыми:

Если параметров несколько, то их обычно называют «T1», «T2» или другими буквами: «T», «S».

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

Использование шаблонов функций

Использование шаблонов функций аналогично использованию обычных функций:

Результат:

8
21.434
b

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

   версия с параметрами типа int (max<int>);

   версия с параметрами типа double (max<double>);

   версия с параметрами типа char (max<char>).

Нам не нужно явно указывать тип передаваемых значений (часть <int> в max<int>), компилятор вычисляет это самостоятельно.

Итого

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

У шаблонов функций есть несколько недостатков, и было бы не простительно, если бы мы о них не поговорили:

   Во-первых, некоторые старые компиляторы могут не поддерживать шаблоны функций или поддерживать, но с ограничениями. Однако сейчас это уже не такая проблема, как раньше.

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

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

Данные недостатки довольно незначительны по сравнению с мощью и гибкостью шаблонов функций!

Примечание: Стандартная библиотека C++ имеет в своем арсенале шаблон функции max() (который находится в заголовочном файле algorithm), поэтому вы можете не реализовывать эту функцию вручную в будущем. Плюс, если вы пишете свои собственные шаблоны функций и используете стейтмент using namespace std;, то не забывайте о возможности возникновения конфликта имён, так как компилятор не сможет определить, хотите ли вы использовать свою версию функции max() или версию std::max().

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (5 оценок, среднее: 5,00 из 5)
Загрузка...

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

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

ВОЛШЕБНАЯ ТАБЛЕТКА ПО С++