Шаблони (C++)

Шаблони (англ. template) — засіб мови C++, який призначений для кодування узагальнених алгоритмів, без прив'язки до деяких параметрів: типу даних, розміру буфера та стандартного значення. В C++ можливе створення шаблону функції і шаблону класу. Хоча шаблони надають коротку форму запису ділянки коду, насправді їх використання не скорочує виконуваний код, тому що для кожного набору параметрів компілятор створює окремий екземпляр функції або класу.

Шаблонні функції

При створенні функцій іноді виникають ситуації, коли дві функції виконують однакову послідовність дій, але працюють з різними типами даних (наприклад, одна використовує параметри типу int, а інша типу float або double). За допомогою механізму перевантаження функцій можна використовувати одне й те ж ім'я для функцій, що виконують різні дії і мають різні типи параметрів. Однак, функції не можуть відрізняються лише типом значення, що повертається, тому що тоді, згідно стандарту, вважається, що у них однакова сигнатура. В цьому випадку потрібно буде використовувати унікальні імена для кожного варіанту. Шаблонна функція дає можливість оперувати параметрами різних типів і повертати значення різних типів, що значно полегшує роботу і зменшує кількість коду. Іншими словами шаблонна функція являє собою набір функцій.

Наприклад, стандартна бібліотека C++ має шаблонну функцію max(x, y), яка повертає більше з двох значень (x або y). Функцію max() можна оголосити з використанням шаблонних типів, таким чином:

template <typename Type>
Type max(Type a, Type b) {
    return a > b ? a : b;
}

Одна ця функція може працювати з різними типами даних. Хоча використання шаблонної функції економить кількість коду, в порівнянні з написанням множини окремих функцій для кожного типу параметрів, вона не зменшує об'єм скомпільованого коду. Наприклад, якщо в програмі функція використовується для двох ситуацій, де параметрами є значення типу int і double, компілятор скомпілює об'єктний код з двома версіями функції max(), одна з яких приймає параметри типу int, а друга double. Результат компіляції буде ідентичний тому, якби ми створили дві окремі функції, замість використання одної шаблонної функції.

#include <iostream>
 
int main(int, char**)
{
  // Зробить виклик функції max <int> (за типом аргументів)
  std::cout << max(3, 7) << std::endl;
  // Зробить виклик функції  max<double> (за типом аргументів)
  std::cout << max(3.0, 7.0) << std::endl;
  // Ця ситуація є неоднозначною, тому слід явно вказати, що слід викликати max<double>
  std::cout << max<double>(3, 7.0) << std::endl;
  return 0;
}

Огляд

Шаблон функції

Оголошення та визначення шаблону функції починається ключовим словом template, слідом за яким йде укладений в кутові дужки і розділений комами непорожній список параметрів шаблону. Цю частину оголошення або визначення зазвичай називають заголовком шаблону.

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

Наприклад, визначений нижче шаблон функції повертає як результат максимальне значення двох параметрів a та b:

template <typename T>
T max(T a, T b) {
    return a > b ? a : b;
}

Тип обох параметрів навмисно залишено не визначеним, натомість він буде визначений параметром шаблону T[1].

Конкретне значення параметру шаблону буде визначено при використанні шаблону функції. Ним може бути будь-який тип даних, який підтримує ті операції, які використані в шаблоні. В наведеному вище прикладі, цей тип має підтримувати оператор «менше» (operator<) а також цей тип має дозволяти операцію копіювання (для повернення результату із тіла функції)[1].

До появи стандарту C++98 для оголошення параметрів шаблону слід було використовувати ключове слово class. Але в новіших версіях мови програмування C++ обидва ключових слова мають в даному контексті однакове значення, тому шаблон:

template <class T>
T max(T a, T b) {
    return a > b ? a : b;
}

ідентичний шаблону з використанням ключового слова typename[1].

Процес заміни параметрів шаблону на конкретні типи називають створенням екземпляру, конкретизацією шаблону (англ. instantiation), а його результат екземпляром шаблону (англ. template instance)[2].

Слід зазначити, що попри те, що мова програмування C++ надає можливість спеціалізувати шаблони функції, через складні правила перевантаження функцій в присутності шаблону функції (з не завжди очікуваними результатами), цією можливістю радять не користуватись. Натомість, для спеціалізації шаблону функції краще визначити перевантажену функцію з потрібною сигнатурою[3].

Шаблон класу

Шаблон класу оголошують так само як і шаблон функції: слід оголосити параметри шаблону з ідентифікаторами використаних типів. Поширеним ідентифікатором типу служить літера T:

template <typename T>
class Name
{
// тіло шаблону класу
};

Аналогічно шаблону функції, замість ключового слова typename можна використати ключове слово class:

template <class T>
class Name
{
// тіло шаблону класу
};

Обидва варіанти будуть еквівалентні[4].

Шаблон змінної

Стандарт C++14 додав можливість оголошувати змінні параметризовані за типом даних[5].

Наприклад, число π можна визначити як шаблон змінної без визначення конкретного типу:

template<typename T> constexpr T pi{3.1415926535897932385};

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

Параметри шаблонів

Кожен параметр шаблону складається з службового слова class, за яким йде ідентифікатор.

У контексті оголошення шаблону функції, службове слово class не має жодного особливого смислового навантаження. Справа в тому, що аналогічна конструкція використовується також і для оголошення шаблону класу, де ключове слово class грає свою особливу роль.

У заголовку шаблону імена параметрів шаблону повинні бути унікальні. Параметрами шаблонів можуть бути: параметри-типи, параметри звичайних типів, параметри-шаблони. Для параметрів будь-якого типу можна вказувати значення за замовчуванням.

template <class T1, // параметр-тип
           typename T2, // параметр-тип
           int I, // параметр звичайного типу
           T1 DefaultValue, // параметр звичайного типу
           template <class> class T3, // параметр-шаблон
           class Character = char> // параметр за замовчуванням

У версії стандарту C++11 була додана можливість використання шаблонів зі змінним числом параметрів.

Шаблони як члени класів

Проблеми виникають і з членами-шаблонами. Якщо шаблон, який є членом класу, який у свою чергу є параметром шаблону, використовується в цьому шаблоні і не допускає виведення параметрів, то необхідно використовувати кваліфікатор template :

   class A
   {
   / * ... * /
   public:
     template <class T> T & ConvertTo ();
     template <class T> void ConvertFrom (const T & data);
   };
   template <class T>
   void f (T Container)
   {
     int i1 = Container.template ConvertTo <int> () + 1;
     Container.ConvertFrom (i1); // кваліфікатор не потрібен
   }

Примітки

  1. (Vandevoorde et al; 1.1.1 Defining the Template)
  2. (Vandevoorde et al; 1.1.2 Using the Template)
  3. Herb Sutter (July 2001). Why Not Specialize Function Templates?.
  4. (Vandevoorde et al; 2.1.1 Declaration of Class Templates)
  5. (Vandevoorde et al; 5.6 Variable Templates)

Література

  • David Vandevoorde, Nicolai M. Josuttis, Douglas Gregor (2018). C++ TemplatesThe Complete Guide (вид. 2ге). Addison-Wesley. ISBN 978-0-321-71412-1.

Див. також

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.