- •Лабораторна робота № 33
- •Тема: Шаблони (параметризовані типи).
- •Мета: вивчити представлення та правила роботи із шаблонами.
- •Теоретична частина
- •Шаблони функцій
- •Вимоги до фактичних параметрів шаблону
- •Ототожнення типів аргументів
- •Шаблони класів
- •Шаблони класів : не лише для типів
- •Успадкування в шаблонах класів
- •Контрольні питання
- •Порядок виконання лабораторної роботи
- •Вимоги до звіту
- •Варіанти завдань
Вимоги до фактичних параметрів шаблону
Шаблон функції toPower() може бути використаний майже для будь-якого типу даних. Застереження "майже" виникає з характеру операцій, що виконуються над параметром base і змінній result в тілі функції toPower(). Який би тип ми не використали у функції toPower(), ці операції для неї мають бути визначені. Інакше компілятор не знатиме, що йому робити. Ось список дій, що виконуються у функції toPower() із змінними base і result, :
T result = base;
return #001;
return #000;
result *= base;
return result;
Усі ці дії визначені для вбудованих типів. Проте якщо ви створите функцію toPower() для якого-небудь класового типу, то в цьому випадку такий клас повинен буде включати загальнодоступні належні функції, які забезпечують наступні можливості, :
дія 1 ініціалізував об'єкт типу Т таким чином, що клас Т повинен містити конструктор копіювання
дії 2 і 3 перетворять значення типу int в об'єкт типу Т, тому клас Т повинен містити конструктор з параметром типу int, оскільки саме таким способом в класах реалізується перетворення до класових типів
дія 4 використовує операцію *= над типом Т, тому клас повинен містити власну функцию- operator *=().
дія 5 припускає, що в типі T передбачена можливість побудови безпечної копії повертаного об'єкту (див. конструктор копіювання).
Схема такого класу виглядає таким чином:
class T
{ public:
T (const T &base); // конструктор копіювання
T (int i); //приведення int до Т
operator *= (T base);
// ... // .. інші методи
}
Використовуючи класи в шаблонах функцій, переконаєтеся в тому, що ви знаєте, які дії з ними виконуються в шаблоні функції, і чи визначені для класу ці дії. Якщо ви не забезпечили клас необхідними функціями, виникнуть різні незрозумілі повідомлення про помилки.
Ототожнення типів аргументів
Оскільки компілятор генерує екземпляри шаблонів функцій згідно з типами, заданими при їх викликах, то критичним моментом є передача коректних типів, особливо якщо шаблон функції має два або більш за параметри. Хорошим прикладом є класична функція max() :
template <class T>
T max (T a, T b)
{ return a > b ? a: b;
}
Функція max() працюватиме правильно, якщо обидва її аргументи мають один і той же тип:
int i = max (1, 2);
double d = max (1.2, 3.4);
Проте, якщо аргументи різних типів, то виклик max() приведе до помилки, оскільки компілятор не зможе зрозуміти, що йому робити.
Один з можливих способів для дозволу неоднозначності полягає у використанні приведення типів, щоб прояснити наші наміри:
int i = max ((int)'a ', 100);
Друга можливість - це явно оголосити версію екземпляра шаблону функції перед її викликом:
int max (int, int);
int j = max ('a ', 100);
Третій спосіб розв'язати проблему полягає в створенні шаблону функцій, який має параметри різних типів.
template <class T1, class T2>
T1 max (T1 a, T2 b)
{ return a > (T1) b ? a: (T1) b;
}
Використання цієї нової версії max() не приведе до неоднозначності у разі використання двох різних типів. Наприклад, якщо написати
max ('a ', 100);
те компілятор використовуватиме два заданих (за допомогою аргументів типу) і побудує версію функції max() із заголовком
char max (char, int);
Далі компілятор перед виконанням порівняння приведе тип другого аргументу до типу першого аргументу. Такий спосіб допустимий, проте використання двох типових параметрів в шаблоні функції, яка повинна була б працювати тільки з одним типом, часто лише утрудняє життя. Досить важко пам'ятати, що
max ('a ', 100)
дає значення типу char, тоді як
max (100, 'a')
передає в зухвалу програму int.
