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

  Юрий  | 

    | 

  Обновл. 27 Июн 2019  | 

 7114

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

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

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

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

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

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

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

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

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

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

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


Сейчас вам, вероятно, интересно, как создаются шаблоны функций в 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 (41 оценок, среднее: 4,98 из 5)
Загрузка...

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

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