Ще один розділ позаду. Пора закріпити пройдений матеріал.
Теорія
Шаблони дозволяють написати одну версію функції або класу, яка працюватиме з різними типами даних. Функція або клас, реалізована через шаблон, з фактичним (одним) типом даних називається екземпляром.
Всі шаблони функцій або шаблони класів повинні починатися з ключового слова template і оголошення параметрів шаблону. В оголошенні параметрів шаблону вказуються параметри типу і параметри non-type.
Параметр типу шаблону — це параметр, який відповідає за типи даних, з якими працюватиме шаблон, зазвичай його називають T
, T1
, T2
або іншими (одиночними) буквами (наприклад, S
).
Параметром non-type може бути змінна інтегрального типу даних (наприклад, char, bool, int, long, short), вказівник/посилання на функцію або на метод/об’єкт класу, std::nullptr_t.
Відокремлення визначення шаблонів класу від його методів по різних файлах не працює як зі звичайними класами — ви не можете помістити визначення шаблону класу в заголовковий файл, а визначення шаблонів методів цього класу в окремий .cpp-файл. Як правило, краще все зберігати в заголовку з визначеннями шаблонів методів під визначенням шаблону класу.
Явна спеціалізація шаблону використовується для визначення реалізації, яка відрізняється від загальної, функції або класу при роботі з певним типом даних. Якщо всі параметри спеціалізації шаблону явно визначені, то це повна спеціалізація. Класи також підтримують часткову спеціалізацію, при якій не всі параметри шаблону повинні бути явно визначені. У C++14 часткова спеціалізація шаблонів функцій заборонена.
Багато класів зі Стандартної бібліотеки C++ (наприклад, std::array та std::vector) використовують шаблони. Шаблони часто застосовуються для реалізації контейнерних класів, які можна один раз написати і використовувати з будь-якими типами даних.
Тест
Завдання №1
Припустимо, що нам потрібно передавати дані парами. Реалізуйте шаблон класу Pair1, який дозволяє користувачеві передавати дані одного типу парами. Наступний код:
1 2 3 4 5 6 7 8 9 10 |
int main() { Pair1<int> p1(6, 9); std::cout << "Pair: " << p1.first() << ' ' << p1.second() << '\n'; const Pair1<double> p2(3.4, 7.8); std::cout << "Pair: " << p2.first() << ' ' << p2.second() << '\n'; return 0; } |
Повинен видавати наступний результат:
Pair: 6 9
Pair: 3.4 7.8
Відповідь №1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <iostream> template <class T> class Pair1 { private: T m_a; T m_b; public: Pair1(const T& a, const T& b) : m_a(a), m_b(b) { } T& first() { return m_a; } const T& first() const { return m_a; } T& second() { return m_b; } const T& second() const { return m_b; } }; int main() { Pair1<int> p1(6, 9); std::cout << "Pair: " << p1.first() << ' ' << p1.second() << '\n'; const Pair1<double> p2(3.4, 7.8); std::cout << "Pair: " << p2.first() << ' ' << p2.second() << '\n'; return 0; } |
Завдання №2
Реалізуйте клас Pair, який дозволяє користувачеві використовувати різні типи даних в переданих парах. Наступний код:
1 2 3 4 5 6 7 8 9 10 |
int main() { Pair<int, double> p1(6, 7.8); std::cout << "Pair: " << p1.first() << ' ' << p1.second() << '\n'; const Pair<double, int> p2(3.4, 5); std::cout << "Pair: " << p2.first() << ' ' << p2.second() << '\n'; return 0; } |
Повинен видавати наступний результат:
Pair: 6 7.8
Pair: 3.4 5
Підказка: Для визначення шаблону з використанням двох різних типів, просто розділіть параметри типу шаблону комою.
Відповідь №2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <iostream> template <class T, class S> class Pair { private: T m_a; S m_b; public: Pair(const T& a, const S& b) : m_a(a), m_b(b) { } T& first() { return m_a; } const T& first() const { return m_a; } S& second() { return m_b; } const S& second() const { return m_b; } }; int main() { Pair<int, double> p1(6, 7.8); std::cout << "Pair: " << p1.first() << ' ' << p1.second() << '\n'; const Pair<double, int> p2(3.4, 5); std::cout << "Pair: " << p2.first() << ' ' << p2.second() << '\n'; return 0; } |
Завдання №3
Напишіть шаблон класу StringValuePair, в якому перше значення завжди типу string, а друге може бути будь-якого типу. Цей шаблон класу повинен наслідувати частково спеціалізований клас Pair (в якому перший параметр типу std::string, а другий — «будь-який тип даних»). Наступний код:
1 2 3 4 5 6 7 |
int main() { StringValuePair<int> svp("Amazing", 7); std::cout << "Pair: " << svp.first() << ' ' << svp.second() << '\n'; return 0; } |
Повинен видавати наступний результат:
Pair: Amazing 7
Підказка: При виклику конструктора класу Pair з конструктора класу StringValuePair, не забудьте вказати, що параметри відносяться до класу Pair.
Відповідь №3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include <iostream> #include <string> template <class T, class S> class Pair { private: T m_a; S m_b; public: Pair(const T& a, const S& b) : m_a(a), m_b(b) { } T& first() { return m_a; } const T& first() const { return m_a; } S& second() { return m_b; } const S& second() const { return m_b; } }; template <class S> class StringValuePair : public Pair<std::string, S> { public: StringValuePair(const std::string& key, const S& value) : Pair<std::string, S>(key, value) { } }; int main() { StringValuePair<int> svp("Amazing", 7); std::cout << "Pair: " << svp.first() << ' ' << svp.second() << '\n'; return 0; } |