Практическая работа №13
Шаблоны C++: Основы обобщенного программирования
Section titled “Шаблоны C++: Основы обобщенного программирования”Цель работы: Изучить и закрепить принципы обобщенного программирования (Generic Programming) в C++ с использованием шаблонов. Освоить синтаксис и применение функциональных и классовых шаблонов, понять механизм вывода типов, а также научиться использовать специализацию шаблонов для работы с особыми типами данных.
Основные теоретические положения
Section titled “Основные теоретические положения”1. Что такое Шаблоны (Templates)
Section titled “1. Что такое Шаблоны (Templates)”Шаблоны — это механизм C++, позволяющий писать код, который может работать с любыми типами данных, не зная их заранее. Это основа обобщенного программирования, которое позволяет создавать универсальные функции и классы, избегая дублирования кода для разных типов (например, max(int, int), max(double, double), max(string, string)).
В C++ существует два основных вида шаблонов:
- Функциональные шаблоны (Function Templates): Создают семейство функций, которые могут обрабатывать аргументы разных типов.
- Классовые шаблоны (Class Templates): Создают семейство классов (контейнеров, структур данных), которые могут хранить и обрабатывать данные разных типов.
2. Функциональные Шаблоны
Section titled “2. Функциональные Шаблоны”Функциональный шаблон определяется с помощью ключевого слова template с последующим списком параметров шаблона в угловых скобках <>. Параметры шаблона могут быть:
- Параметры-типы (
typename Tилиclass T): Представляют любой тип данных. - Параметры-константы (Non-type parameters): Представляют константные значения (например, целые числа, используемые для размера массива).
template <typename T>T maximum(T a, T b) { return (a > b) ? a : b;}Вывод типов (Type Deduction): Компилятор автоматически определяет тип T на основе типов аргументов, переданных при вызове функции.
3. Классовые Шаблоны
Section titled “3. Классовые Шаблоны”Классовые шаблоны используются для создания обобщенных структур данных, таких как списки, векторы, пары или очереди.
template <typename T1, typename T2>class Pair {public: T1 first; T2 second; Pair(T1 a, T2 b) : first(a), second(b) {}};При создании объекта классового шаблона необходимо явно указать типы: Pair<int, double> p(10, 3.14);.
4. Специализация Шаблонов
Section titled “4. Специализация Шаблонов”Специализация позволяет предоставить отдельную, оптимизированную или специфическую реализацию для конкретного типа или набора типов.
- Полная специализация (Full Specialization): Предоставляет реализацию для конкретного типа. Например, для
maximum<const char*>(чтобы сравнивать строки, а не адреса). - Частичная специализация (Partial Specialization): Применяется только к классовым шаблонам. Предоставляет реализацию для семейства типов, где некоторые параметры шаблона фиксированы или имеют определенную форму (например, для всех указателей
T*).
// Частичная специализация классового шаблона Pair для указателейtemplate <typename T>class Pair<T*, T*> { // Специальная реализация для пары указателей};Алгоритмы и шаблоны реализации
Section titled “Алгоритмы и шаблоны реализации”1. Алгоритм создания Функционального Шаблона
Section titled “1. Алгоритм создания Функционального Шаблона”- Определить функцию, которая должна быть обобщенной.
- Перед заголовком функции добавить объявление шаблона:
template <typename T, ...>. - Заменить конкретные типы данных (например,
int,double) на параметр-типT. - Реализовать логику функции, используя
T.
2. Алгоритм создания Классового Шаблона
Section titled “2. Алгоритм создания Классового Шаблона”- Определить класс, который должен быть обобщенным (например, контейнер).
- Перед объявлением класса добавить объявление шаблона:
template <typename T, ...>. - Заменить типы членов класса и методов на параметр-тип
T. - При необходимости, реализовать частичную специализацию для особых случаев (например, для указателей).
Примеры реализации (C++)
Section titled “Примеры реализации (C++)”Пример 1 — Функциональный Шаблон swap
Section titled “Пример 1 — Функциональный Шаблон swap”#include <iostream>#include <string>
// 1. Объявление функционального шаблонаtemplate <typename T>void swap_values(T& a, T& b) { T temp = a; a = b; b = temp;}
int main() { int i1 = 5, i2 = 10; double d1 = 3.14, d2 = 2.71; std::string s1 = "Hello", s2 = "World";
swap_values(i1, i2); // T = int swap_values(d1, d2); // T = double swap_values(s1, s2); // T = std::string
std::cout << "Ints: " << i1 << ", " << i2 << "\n"; std::cout << "Doubles: " << d1 << ", " << d2 << "\n"; std::cout << "Strings: " << s1 << ", " << s2 << "\n";
return 0;}Пример 2 — Классовый Шаблон Container
Section titled “Пример 2 — Классовый Шаблон Container”#include <iostream>
// 2. Объявление классового шаблонаtemplate <typename T>class Container {private: T value;public: Container(T val) : value(val) {}
void print() const { std::cout << "Value: " << value << "\n"; }
T get_value() const { return value; }};
int main() { Container<int> c_int(42); Container<std::string> c_str("Template Power"); Container<double> c_double(9.81);
c_int.print(); c_str.print(); c_double.print();
return 0;}Пример 3 — Частичная специализация для указателей
Section titled “Пример 3 — Частичная специализация для указателей”#include <iostream>#include <string>
// Основной классовый шаблонtemplate <typename T>class SmartPrinter {private: T data;public: SmartPrinter(T d) : data(d) {} void print() const { std::cout << "Generic data: " << data << "\n"; }};
// 3. Частичная специализация для всех указателей (T*)template <typename T>class SmartPrinter<T*> {private: T* data;public: SmartPrinter(T* d) : data(d) {} void print() const { if (data) { std::cout << "Pointer to " << typeid(T).name() << ". Address: " << data << ", Value: " << *data << "\n"; } else { std::cout << "Null pointer.\n"; } }};
int main() { int x = 100; int* ptr_x = &x;
SmartPrinter<int> p1(50); SmartPrinter<int*> p2(ptr_x); // Используется частичная специализация
p1.print(); p2.print();
return 0;}Общие требования
Section titled “Общие требования”- Написать программу на языке C++ (стандарт C++11 или выше).
- Реализовать:
- Минимум один функциональный шаблон с явным и неявным выводом типов.
- Минимум один классовый шаблон с двумя параметрами-типами.
- Частичную специализацию для классового шаблона (например, для указателей или пар одинаковых типов).
- Параметр-константу (Non-type parameter) в классовом шаблоне.
- Обработка ввода/вывода данных через
std::cin/std::coutдля пользовательского взаимодействия. - Комментарии в коде, поясняющие ключевые места:
template <typename T>,template <typename T1, typename T2>, полная и частичная специализация.
Вывод программы (что должно отображаться)
Section titled “Вывод программы (что должно отображаться)”- Демонстрация вызова функционального шаблона с разными типами (
int,double,std::string). - Демонстрация создания объектов классового шаблона с разными типами.
- Четкое подтверждение того, что была вызвана специализированная версия шаблона.
- Демонстрация работы классового шаблона с параметром-константой.
Задания для выполнения
Section titled “Задания для выполнения”Базовый уровень
Section titled “Базовый уровень”| № | Задача | Описание |
|---|---|---|
| 1 | Функциональный шаблон | Создайте функциональный шаблон print_info(T value), который выводит значение и имя типа (используя typeid(T).name()). Проверьте его для int, double и char. |
| 2 | Классовый шаблон | Реализуйте классовый шаблон Triple<T1, T2, T3>, который хранит три значения разных типов. Добавьте метод print_all(). |
| 3 | Параметр-константа | Создайте классовый шаблон FixedSizeBuffer<T, N>, где N — это размер буфера (массива) типа T. Реализуйте конструктор и метод get_size(). |
Повышенный уровень
Section titled “Повышенный уровень”| № | Задача | Описание |
|---|---|---|
| 4 | Частичная специализация | Добавьте к классовому шаблону Triple<T1, T2, T3> из Задания 2 частичную специализацию Triple<T, T, T> (когда все три типа одинаковы). В специализированной версии метод print_all() должен выводить дополнительное сообщение: “Все элементы одного типа!“. |
| 5 | Полная специализация | Создайте функциональный шаблон compare(T a, T b), который сравнивает два значения. Добавьте полную специализацию для типа const char*, чтобы сравнение выполнялось с помощью strcmp (лексикографически), а не по адресам. |
| 6 | Шаблон-фабрика | Создайте функциональный шаблон create_object(Args... args), который использует вариативные шаблоны (variadic templates) и perfect forwarding для создания объекта любого класса T с помощью new T(std::forward<Args>(args)...). |
Контрольные вопросы
Section titled “Контрольные вопросы”- В чём основное преимущество использования шаблонов в C++?
- Объясните разницу между функциональным и классовым шаблоном.
- Что такое вывод типов (Type Deduction) и когда он не работает?
- В чём разница между полной и частичной специализацией шаблонов?
- Для чего используются параметры-константы (Non-type template parameters)? Приведите пример.
- Почему при работе с шаблонами часто используют ключевое слово
typename?
Критерии оценки
Section titled “Критерии оценки”- Полная реализация базовых заданий (1-3): 50%.
- Корректная реализация и демонстрация частичной специализации (Задание 4): 20%.
- Корректная реализация полной специализации (Задание 5): 15%.
- Чистота кода, комментарии и ответы на контрольные вопросы: 15%.
Рекомендуемая литература
Section titled “Рекомендуемая литература”- Бьерн Страуструп — Язык программирования C++. (Глава 23: Шаблоны)
- Скотт Мейерс — Effective C++. (Раздел о шаблонах и обобщенном программировании)
- Дэвид Вандевурд, Николаи М. Джосаттис — Шаблоны C++: Справочник разработчика. (Наиболее полное руководство по шаблонам)
- cppreference.com — Разделы “Templates” и “Template specialization” (Онлайн-справочник).
- C++ Core Guidelines — Раздел T: Templates and generic programming.