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

  Юрій  | 

  Оновл. 24 Бер 2021  | 

 160

На попередніх уроках ми розглянули, як за допомогою функцій і класів зробити програми зручнішими, безпечнішими і продуктивнішими.

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

Хоча функції і класи є потужними і гнучкими інструментами для ефективного програмування, в деяких випадках вони обмежені через вимоги 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 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

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