Шаблоны в C++

Глава 5. Продвинутые концепции #

5.1 Шаблоны в C++ #

Шаблоны являются мощным механизмом обобщенного программирования в C++, позволяющим создавать универсальный код, работающий с различными типами данных.

1. Функции-шаблоны #

1.1 Базовый синтаксис #

// Шаблонная функция для обмена значений
template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5, y = 10;
    swap(x, y);  // Работает с int
    
    double a = 3.14, b = 2.71;
    swap(a, b);  // Работает с double
    
    return 0;
}

1.2 Ключевые особенности #

  • Ключевое слово template
  • Параметр типа указывается с typename или class
  • Компилятор автоматически генерирует код для каждого используемого типа

1.3 Функции с несколькими параметрами типа #

template <typename T1, typename T2>
void printPair(T1 first, T2 second) {
    std::cout << "First: " << first 
              << ", Second: " << second << std::endl;
}

int main() {
    printPair(42, "Hello");      // int и const char*
    printPair(3.14, std::string("Pi")); // double и string
    return 0;
}

2. Классы-шаблоны #

2.1 Определение шаблонного класса #

template <typename T>
class Stack {
private:
    std::vector<T> elements;

public:
    void push(const T& item) {
        elements.push_back(item);
    }

    T pop() {
        if (!elements.empty()) {
            T top = elements.back();
            elements.pop_back();
            return top;
        }
        throw std::out_of_range("Стек пуст");
    }

    bool isEmpty() const {
        return elements.empty();
    }
};

int main() {
    Stack<int> intStack;
    intStack.push(10);
    intStack.push(20);

    Stack<std::string> stringStack;
    stringStack.push("Привет");
    stringStack.push("Мир");

    return 0;
}

2.2 Параметры по умолчанию #

template <typename T = int>
class Container {
private:
    T value;

public:
    Container(T val = T()) : value(val) {}
};

int main() {
    Container<>  defaultContainer;     // int по умолчанию
    Container<double> doubleContainer; // double
    return 0;
}

3. Шаблоны с несколькими параметрами #

3.1 Сложные шаблонные конструкции #

template <typename K, typename V>
class Map {
private:
    std::vector<std::pair<K, V>> data;

public:
    void insert(const K& key, const V& value) {
        data.push_back({key, value});
    }

    V& operator[](const K& key) {
        for (auto& item : data) {
            if (item.first == key) {
                return item.second;
            }
        }
        // Вставка нового элемента, если ключ не найден
        data.push_back({key, V()});
        return data.back().second;
    }
};

int main() {
    Map<std::string, int> ages;
    ages.insert("Алиса", 30);
    ages["Боб"] = 25;

    Map<int, std::string> names;
    names.insert(1, "Первый");
    names[2] = "Второй";

    return 0;
}

3.2 Ограничения и требования к типам #

// Шаблонная функция с ограничением на копируемость
template <typename T>
requires std::copyable<T>
T duplicateValue(const T& value) {
    return value;
}

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

4.1 Полная специализация #

// Общий шаблон
template <typename T>
class Printer {
public:
    void print(const T& value) {
        std::cout << "Общий шаблон: " << value << std::endl;
    }
};

// Полная специализация для bool
template <>
class Printer<bool> {
public:
    void print(bool value) {
        std::cout << "Специализация для bool: " 
                  << (value ? "true" : "false") << std::endl;
    }
};

int main() {
    Printer<int> intPrinter;
    intPrinter.print(42);  // Общий шаблон

    Printer<bool> boolPrinter;
    boolPrinter.print(true);  // Специализация для bool

    return 0;
}

4.2 Частичная специализация #

// Общий шаблон для указателей
template <typename T>
class Pointer {
public:
    static void print(T* ptr) {
        std::cout << "Указатель: " << ptr << std::endl;
    }
};

// Частичная специализация для указателей на char
template <typename T>
class Pointer<T*> {
public:
    static void print(T* ptr) {
        std::cout << "Указатель на объект: " 
                  << *ptr << std::endl;
    }
};

int main() {
    int x = 42;
    int* ptr = &x;
    
    Pointer<int*>::print(ptr);  // Вызовет специализированную версию

    return 0;
}

Заключение #

Ключевые преимущества шаблонов #

  • Типобезопасность
  • Возможность создания обобщенного кода
  • Генерация кода во время компиляции
  • Минимальные накладные расходы времени выполнения

Рекомендации #

  • Используйте шаблоны для создания универсальных алгоритмов и структур данных
  • Будьте осторожны с усложнением шаблонов
  • Применяйте специализацию для оптимизации под конкретные типы
  • Следите за читаемостью кода при использовании сложных шаблонов

Типичные ошибки #

  1. Чрезмерное использование шаблонов
  2. Сложночитаемые многопараметрические шаблоны
  3. Неправильная специализация
  4. Большой объем сгенерированного кода

Дополнительные материалы #

  • С++ Standard Template Library (STL)
  • Концепты в современном C++ (C++20)
  • SFINAE и метапрограммирование