Основы шаблонов
Тема дорожной карты · 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 превращают простую опечатку пользователя в сотни строк диагностики из недр библиотеки. Наконец, специализация шаблона функции вместо перегрузки участвует в разрешении перегрузки иначе, чем ожидают многие, и предпочтительнее использовать перегрузки.
Связанные понятия
Полезные ресурсы
🔤 Английские термины в этой теме
Слова из этой темы, которые русскоязычные часто произносят неверно. Кнопка 🔊 произносит голосом браузера.
- algorithm /ˈælɡərɪðəm/ ударение на первый слог разбор: Немые буквы и ударение: cache, queue, suite, height
- written /ˈrɪtn/ «ритн», двойное t произносится как одно, i краткое разбор: Письменная коммуникация
- result /rɪˈzʌlt/ ударение на второй слог разбор: Behavioral questions и метод STAR