C++MiddleTechnical
Что такое templates в C++ и что такое template specialization?
Шаблоны генерируют код на этапе компиляции для каждого типа-аргумента. Полная специализация задаёт реализацию для конкретного типа (template<>); частичная специализация (только для классов) — для паттерна типов.
Шаблоны (Templates)
Шаблоны — механизм генерации кода во время компиляции. Компилятор создаёт отдельную копию функции или класса для каждого набора типовых аргументов (instantiation). Это статический полиморфизм без накладных расходов runtime.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
// Функциональный шаблон
template<typename T>
T max_of(T a, T b) {
return (a > b) ? a : b;
}
// Шаблон класса
template<typename T, size_t N>
class FixedArray {
T data_[N];
public:
T& operator[](size_t i) { return data_[i]; }
const T& operator[](size_t i) const { return data_[i]; }
constexpr size_t size() const { return N; }
// Шаблонный метод внутри шаблонного класса
template<typename U>
void fill(U value) {
for (auto& elem : data_) elem = static_cast<T>(value);
}
};
void demo_basic() {
auto m1 = max_of(3, 5); // int
auto m2 = max_of(3.14, 2.71); // double
auto m3 = max_of<std::string>("apple", "banana");
FixedArray<int, 5> arr;
arr.fill(42);
std::cout << arr[0] << '\n'; // 42
}
Полная специализация (Full Specialization)
Полная специализация задаёт конкретную реализацию для конкретного набора аргументов:
// Основной шаблон
template<typename T>
struct TypeInfo {
static const char* name() { return "unknown"; }
};
// Полная специализация для int
template<>
struct TypeInfo<int> {
static const char* name() { return "int"; }
};
// Полная специализация для double
template<>
struct TypeInfo<double> {
static const char* name() { return "double"; }
};
void demo_full_spec() {
std::cout << TypeInfo<int>::name() << '\n'; // "int"
std::cout << TypeInfo<double>::name() << '\n'; // "double"
std::cout << TypeInfo<char>::name() << '\n'; // "unknown"
}
Частичная специализация (Partial Specialization)
Частичная специализация применяется только к шаблонам классов (не функций), позволяет специализировать по паттерну:
// Основной шаблон
template<typename T, typename U>
struct Pair {
T first;
U second;
void print() { std::cout << "generic\n"; }
};
// Частичная специализация: оба типа одинаковы
template<typename T>
struct Pair<T, T> {
T first;
T second;
void print() { std::cout << "same types\n"; }
};
// Частичная специализация для указателей
template<typename T>
struct Pair<T*, T*> {
T* first;
T* second;
void print() { std::cout << "pointers\n"; }
};
void demo_partial_spec() {
Pair<int, double> p1; // generic
Pair<int, int> p2; // same types
Pair<int*, int*> p3; // pointers
p1.print(); // generic
p2.print(); // same types
p3.print(); // pointers
}
Подводные камни
- Code bloat: каждая instantiation генерирует отдельный код — 100 типов = 100 копий функции в бинарнике. Используйте explicit instantiation или type erasure для горячих путей.
- Ошибки компиляции нечитаемы: сообщение об ошибке внутри шаблона выдаёт несколько экранов текста. В C++20
requires(concepts) делает сообщения понятнее. - Частичная специализация функций: функции не поддерживают частичную специализацию — вместо неё используют перегрузку или вспомогательный шаблон класса.
- Порядок специализаций: специализации должны быть объявлены до использования; компилятор выбирает «наиболее специфичную» — неоднозначность приводит к ошибке компиляции.
- typename и template в зависимых контекстах:
typename T::typeиobj.template method<U>()— ключевые слова обязательны, иначе парсер трактует как сравнение. - Экспорт шаблонов в .cpp: тела шаблонов должны быть в заголовочных файлах (или явно instantiated в .cpp) — определение в .cpp без явной instantiation не сгенерирует код для других TU.
- SFINAE vs Concepts: до C++20 для ограничения типов использовали
std::enable_if— сложно и хрупко. В C++20 предпочитайтеrequiresи concepts.
Common mistakes
- Объяснять templates и specialization только по синтаксису, без жизненного цикла и стоимости.
- Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
- Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
- Показывать сырой указатель без объяснения владельца и момента освобождения.
What the interviewer is testing
- Кандидат формулирует точную модель для templates и specialization, а не только определение.
- Пример компилируем, безопасен по lifetime и соответствует версии технологии.
- Названы trade-off, ограничения и диагностируемые симптомы ошибки.