Специализация шаблонов
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 66 из 104.
Иногда универсальный шаблон плохо работает с каждым типом. Например, сравнение C-строк с помощью > сравнивает адреса указателей, а не сам текст. Специализация шаблона позволяет предоставить пользовательскую реализацию для определённых типов, сохраняя универсальную версию для всех остальных.
Полная специализация заменяет весь шаблон для одного конкретного типа. Вы объявляете её с пустым списком параметров шаблона и указываете конкретный тип:
// Основной шаблон
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// Полная специализация для const char*
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "String: " << value << std::endl;
}
};
Printer<int> intPrinter;
intPrinter.print(42); // Вывод: 42
Printer<const char*> strPrinter;
strPrinter.print("hello"); // Вывод: String: helloШаблоны функций также можно специализировать:
template <typename T>
bool isEqual(T a, T b) {
return a == b;
}
template <>
bool isEqual<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
isEqual(5, 5); // Использует обобщённую версию
isEqual("hi", "hi"); // Использует специализированную версиюКомпилятор всегда предпочитает наиболее специфичное соответствие — если существует специализация для используемого точного типа, она будет выбрана вместо generic-шаблона.
Задание
ЛегкоДавайте создадим систему форматирования с учетом типов, которая демонстрирует, как специализация шаблонов позволяет настраивать поведение для определенных типов, сохраняя при этом общий вариант по умолчанию для всех остальных типов.
Вы создадите два файла, чтобы организовать код шаблонов:
Formatter.h: Определите шаблон класса под названиемFormatter, который форматирует значения для отображения. Ваш общий шаблон должен работать с любым типом, но также создайте специализированные версии для типов, требующих особой обработки.Основной шаблон
Formatterдолжен содержать:- Метод
format(), который принимает значение типа T и выводит:Value: <value>
Создайте полную специализацию для
bool, которая выводит понятный человеку текст вместо 1 или 0:- Метод
format()должен выводить:Boolean: trueилиBoolean: false
Создайте полную специализацию для
const char*, которая добавляет кавычки вокруг строк:- Метод
format()должен выводить:String: "<value>"
Также создайте шаблон функции с именем
formatPair, который принимает два значения одного типа и выводит их вместе в виде:Pair: [<first>, <second>]Создайте специализацию
formatPairдляconst char*, которая заключает каждую строку в кавычки:Pair: ["<first>", "<second>"]- Метод
main.cpp: Считайте четыре входных значения (каждое в отдельной строке):- Целое число
- Число с плавающей точкой двойной точности
- Логическое значение в виде строки (
trueилиfalse) - Строковое значение
Продемонстрируйте работу форматтеров, создав соответствующие объекты
Formatterи вызвавformat()для каждого типа:- Отформатируйте целое число с помощью
Formatter<int> - Отформатируйте число двойной точности с помощью
Formatter<double> - Отформатируйте логическое значение с помощью
Formatter<bool>(сначала преобразуйте строковый ввод в bool) - Отформатируйте строку с помощью
Formatter<const char*>
Затем продемонстрируйте специализацию шаблона функции:
- Вызовите
formatPairс двумя целыми числами: 10 и 20 - Вызовите
formatPairс двумя C-строками:"hello"и"world"
Например, при входных значениях 42, 3.14, true и Hello:
Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]Обратите внимание, что общий шаблон одинаково обрабатывает целые числа и числа двойной точности, тогда как специализированные версии для bool и const char* обеспечивают особое форматирование. Компилятор автоматически выбирает наиболее подходящее соответствие для каждого используемого типа.
Попробуйте сами
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// Прочитать входные данные
int intVal;
double doubleVal;
string boolStr;
string strVal;
cin >> intVal;
cin >> doubleVal;
cin >> boolStr;
cin >> strVal;
// Преобразовать строку в bool
bool boolVal = (boolStr == "true");
// TODO: Создать Formatter<int> и отформатировать целое число
// TODO: Создать Formatter<double> и отформатировать double
// TODO: Создать Formatter<bool> и отформатировать логическое значение
// TODO: Создать Formatter<const char*> и отформатировать строку
// Подсказка: Используйте strVal.c_str() чтобы получить const char*
// TODO: Вызвать formatPair с двумя целыми числами: 10 и 20
// TODO: Вызвать formatPair с двумя C-строками: "hello" и "world"
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Итоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++