Menu
Coddy logo textTech

Специализация шаблонов

Часть раздела Объектно-ориентированное программирование путешествия по 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-шаблона.

challenge icon

Задание

Легко

Давайте создадим систему форматирования с учетом типов, которая демонстрирует, как специализация шаблонов позволяет настраивать поведение для определенных типов, сохраняя при этом общий вариант по умолчанию для всех остальных типов.

Вы создадите два файла, чтобы организовать код шаблонов:

  • 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: Считайте четыре входных значения (каждое в отдельной строке):
    1. Целое число
    2. Число с плавающей точкой двойной точности
    3. Логическое значение в виде строки (true или false)
    4. Строковое значение

    Продемонстрируйте работу форматтеров, создав соответствующие объекты Formatter и вызвав format() для каждого типа:

    1. Отформатируйте целое число с помощью Formatter<int>
    2. Отформатируйте число двойной точности с помощью Formatter<double>
    3. Отформатируйте логическое значение с помощью Formatter<bool> (сначала преобразуйте строковый ввод в bool)
    4. Отформатируйте строку с помощью Formatter<const char*>

    Затем продемонстрируйте специализацию шаблона функции:

    1. Вызовите formatPair с двумя целыми числами: 10 и 20
    2. Вызовите 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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++