Основы шаблонов

Тема дорожной карты · Modern C++ Developer

Шаблоны — механизм обобщённого программирования в C++, благодаря которому один и тот же код работает с любыми типами без потери производительности. Вся стандартная библиотека — от std::vector до std::sort — построена на шаблонах. В отличие от дженериков в Java или C#, шаблон C++ инстанцируется на этапе компиляции для каждого набора аргументов, поэтому компилятор видит конкретные типы и оптимизирует код так же, как написанный вручную. Это фундамент, на который опираются концепты, метапрограммирование и большинство современных библиотек.

Как это работает

Шаблон функции объявляется через template <typename T> и описывает семейство функций, из которого компилятор порождает конкретную версию при первом использовании. Аргументы шаблона обычно выводятся из аргументов вызова: для max(3, 5) компилятор выведет T = int. Шаблон класса требует явного указания параметров до C++17, а начиная с C++17 работает вывод аргументов конструктора (CTAD): std::pair p{1, 2.0} создаёт std::pair<int, double>.

Помимо типов, параметрами могут быть целочисленные константы и указатели (non-type template parameters) — так std::array<int, 16> знает свой размер на этапе компиляции. Шаблоны можно специализировать: полная специализация задаёт отдельную реализацию для конкретного типа, частичная — для семейства типов, например всех указателей. Именно так std::hash или std::numeric_limits настраиваются под каждый тип.

Определение шаблона обязано быть видимым в точке инстанцирования, поэтому шаблонный код живёт в заголовочных файлах. Компилятор проверяет синтаксис при разборе шаблона, а корректность операций над T — только при подстановке конкретного типа. Отсюда знаменитые многоэкранные сообщения об ошибках, которые в C++20 существенно сокращают концепты.

Когда применять

Шаблоны уместны, когда алгоритм или структура данных не зависит от конкретного типа: контейнеры, математические утилиты, сериализация, обёртки над ресурсами. Они заменяют дублирование кода и виртуальные вызовы там, где полиморфизм известен на этапе компиляции. Шаблоны с числовыми параметрами позволяют выносить в компиляцию размеры буферов и размерности матриц. Если же набор типов фиксирован и известен заранее, часто проще перегрузка функций или std::variant; а когда нужна динамическая подмена реализации во время выполнения — виртуальные функции.

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

Чрезмерная обобщённость делает код нечитаемым и раздувает время компиляции: каждая инстанциация — это отдельная копия кода в объектном файле. Определение шаблона в .cpp-файле приводит к ошибкам компоновки «undefined reference», потому что компилятор не видит его в точке использования. Забытое ключевое слово typename перед зависимым именем типа (typename T::iterator) даёт непонятные ошибки разбора. Неявные требования к T без концептов или static_assert превращают простую опечатку пользователя в сотни строк диагностики из недр библиотеки. Наконец, специализация шаблона функции вместо перегрузки участвует в разрешении перегрузки иначе, чем ожидают многие, и предпочтительнее использовать перегрузки.

Связанные понятия

Полезные ресурсы

🔤 Английские термины в этой теме

Слова из этой темы, которые русскоязычные часто произносят неверно. Кнопка 🔊 произносит голосом браузера.

Весь словарь терминов →