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");        // Использует специализированную версию

Компилятор всегда предпочитает наиболее специфичное совпадение — если для используемого конкретного типа существует специализация, она будет выбрана вместо общего шаблона.

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. Целое число (int)
    2. Число с плавающей точкой (double)
    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* обеспечивают настраиваемое форматирование. Компилятор автоматически выбирает наиболее подходящее совпадение для каждого используемого вами типа.

Шпаргалка

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

Синтаксис полной специализации

Объявите специализацию с пустым списком параметров шаблона template <> и укажите конкретный тип:

// Основной шаблон
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;
    }
};

Специализация шаблонов функций

Шаблоны функций также могут быть специализированы:

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;
}

Выбор компилятора

Компилятор всегда предпочитает наиболее точное совпадение — если для используемого типа существует специализация, она будет выбрана вместо общего шаблона.

Попробуйте сами

#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> и отформатируйте число с плавающей запятой
    
    // TODO: Создайте Formatter<bool> и отформатируйте логическое значение
    
    // TODO: Создайте Formatter<const char*> и отформатируйте строку
    // Hint: Используйте strVal.c_str(), чтобы получить const char*
    
    // TODO: Вызовите formatPair с двумя целыми числами: 10 и 20
    
    // TODO: Вызовите formatPair с двумя C-строками: "hello" и "world"
    
    return 0;
}
quiz iconПроверьте себя

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

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