Шаблоны функций
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 64 из 104.
Представьте, что вы пишете функцию для поиска максимума из двух целых чисел, а затем понимаете, что вам нужна та же логика для чисел типа double и снова для строк. Без шаблонов вам пришлось бы писать почти идентичные функции для каждого типа. Шаблоны функций решают эту проблему, позволяя вам написать логику один раз, после чего компилятор автоматически создаст версии для конкретных типов.
Шаблон функции использует ключевое слово template, за которым следуют параметры шаблона в угловых скобках:
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << maximum(5, 3) << std::endl; // Использует версию int
std::cout << maximum(3.14, 2.71) << std::endl; // Использует версию double
std::cout << maximum('a', 'z') << std::endl; // Использует версию char
}Компилятор проверяет каждый вызов и генерирует конкретную функцию для этого конкретного типа. Этот процесс называется инстанцированием шаблона. Вы также можете явно указать тип, когда это необходимо:
std::cout << maximum<double>(5, 3.14) << std::endl; // Принудительно использует версию doubleШаблоны могут иметь несколько параметров типа, что обеспечивает еще более гибкое проектирование:
template <typename T, typename U>
void printPair(T first, U second) {
std::cout << first << ", " << second << std::endl;
}
printPair(42, "hello"); // T=int, U=const char*
printPair(3.14, 100); // T=double, U=intШаблоны функций обеспечивают полиморфизм на этапе компиляции — тип определяется во время компиляции кода, а не во время выполнения. Это означает отсутствие накладных расходов во время выполнения по сравнению с виртуальными функциями, что делает шаблоны идеальными для критически важного для производительности обобщенного кода.
Задание
ЛегкоДавайте создадим набор утилит, используя шаблоны функций, чтобы реализовать переиспользуемые операции, работающие с любым совместимым типом. Вы организуете свои универсальные функции в заголовочном файле и продемонстрируете их гибкость в основной программе.
Вы создадите два файла:
MathUtils.h: Определите коллекцию шаблонов функций, которые выполняют общие операции:minimum— шаблонная функция, которая принимает два значения одного типа и возвращает меньшее из них.clamp— шаблонная функция, принимающая три параметра: значение, нижнюю границу и верхнюю границу. Она возвращает значение, ограниченное этими рамками (возвращает нижнюю границу, если значение меньше неё, верхнюю границу, если значение больше неё, в противном случае возвращает само значение).swapValues— шаблонная функция, которая принимает две ссылки одного типа и обменивает их значения.main.cpp: Считайте шесть входных значений (каждое на отдельной строке):- Первое целое число
- Второе целое число
- Значение типа double для ограничения (clamp)
- Нижняя граница (double)
- Верхняя граница (double)
- Символ
Продемонстрируйте работу ваших шаблонов следующим образом:
- Найдите минимум из двух целых чисел и выведите:
Min of <a> and <b>: <result> - Найдите минимум из символа
'm'и введенного вами символа, выведя:Min of m and <char>: <result> - Ограничьте значение double и выведите:
Clamp <value> to [<low>, <high>]: <result> - Ограничьте первое целое число диапазоном [0, 100] и выведите:
Clamp <value> to [0, 100]: <result> - Поменяйте местами два целых числа и выведите:
After swapValues: <a>, <b>
Например, при входных данных 25, 10, 3.7, 1.0, 5.0 и z:
Min of 25 and 10: 10
Min of m and z: m
Clamp 3.7 to [1, 5]: 3.7
Clamp 25 to [0, 100]: 25
After swapValues: 10, 25Обратите внимание, как каждая шаблонная функция беспрепятственно работает с целыми числами, числами с плавающей точкой и символами — компилятор генерирует соответствующую версию для каждого используемого вами типа. Ваша функция swapValues должна изменять исходные переменные через ссылки, демонстрируя, что шаблоны работают с параметрами-ссылками точно так же, как и обычные функции.
Шпаргалка
Шаблоны функций позволяют писать универсальные функции, которые работают с несколькими типами. Используйте ключевое слово template, за которым следуют параметры шаблона:
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}Компилятор автоматически генерирует версии для конкретных типов посредством инстанцирования шаблона:
maximum(5, 3); // Генерирует версию для int
maximum(3.14, 2.71); // Генерирует версию для double
maximum('a', 'z'); // Генерирует версию для charПри необходимости вы можете явно указать тип:
maximum<double>(5, 3.14); // Принудительно использует версию для doubleШаблоны поддерживают несколько параметров типа:
template <typename T, typename U>
void printPair(T first, U second) {
std::cout << first << ", " << second << std::endl;
}
printPair(42, "hello"); // T=int, U=const char*Шаблоны работают с параметрами-ссылками для изменения исходных значений:
template <typename T>
void swapValues(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}Шаблоны функций обеспечивают полиморфизм на этапе компиляции с нулевыми накладными расходами во время выполнения, что делает их идеальными для критически важного с точки зрения производительности универсального кода.
Попробуйте сами
#include <iostream>
#include "MathUtils.h"
using namespace std;
int main() {
// Чтение входных данных
int a, b;
double value, low, high;
char ch;
cin >> a;
cin >> b;
cin >> value;
cin >> low;
cin >> high;
cin >> ch;
// TODO: Используйте шаблон minimum, чтобы найти минимум двух целых чисел
// Вывод: "Min of <a> and <b>: <result>"
// TODO: Используйте шаблон minimum, чтобы найти минимум 'm' и введенного символа
// Вывод: "Min of m and <char>: <result>"
// TODO: Используйте шаблон clamp для значения типа double
// Вывод: "Clamp <value> to [<low>, <high>]: <result>"
// TODO: Используйте шаблон clamp для первого целого числа с диапазоном [0, 100]
// Вывод: "Clamp <value> to [0, 100]: <result>"
// TODO: Используйте шаблон swapValues для двух целых чисел
// Вывод: "After swapValues: <a>, <b>"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер