Перейти к содержанию

Метапрограммирование шаблонов

Начало урока

Метапрограммирование шаблонов

Превращаем компилятор в программу, которая вычисляет типы и значения ещё до запуска вашего кода

Главная идея: Шаблон C++ — это не просто способ не копировать класс под каждый тип. Это маленький функциональный язык программирования, работающий внутри компилятора. Метапрограммирование шаблонов (TMP) означает написание кода, задача которого — вычислять типы и константные значения во время компиляции, чтобы машинный код для этапа выполнения был уже специализирован, проверен и свободен от решений, которые иначе стоили бы времени или безопасности во время выполнения.
К концу вы сможете:
  • объяснить двухфазную модель: что вычисляет компилятор, а что — процессор;
  • переносить вычисления на этап компиляции с помощью функций constexpr и consteval (C++20);
  • исследовать и преобразовывать типы с помощью библиотеки <type_traits>;
  • ветвиться по типам внутри одной функции через if constexpr вместо запутанных перегрузок;
  • обеспечивать контракты на этапе компиляции через static_assert и понимать реальные компромиссы TMP.

1. Два мира: время компиляции и время выполнения

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

Шаблоны вы уже знаете из промежуточного курса: template<typename T> позволяет одному определению обслуживать много типов. Ключевая мысль TMP в том, что инстанцирование шаблона само по себе является вычислением. Когда вы пишете std::vector<int>, компилятор запускает шаблон вектора с аргументом int и создаёт совершенно новый тип. Дайте шаблону другие аргументы — и он вычислит другие типы: это чистая функция из типов (и значений) в типы (и значения), исполняемая компилятором.

Зачем это нужно? Три конкретные выгоды:

  • Скорость: значение, вычисленное на этапе компиляции, становится литералом в бинарнике; процессор не пересчитывает его.
  • Безопасность: ошибка, пойманная при компиляции, никогда не дойдёт до пользователя. static_assert превращает логическую ошибку в ошибку сборки.
  • Обобщённость без затрат: один обобщённый алгоритм специализируется под каждый тип с нулевым ветвлением во время выполнения.
Правило: если ответ зависит только от информации, которая уже есть у компилятора (типы, литералы, размеры), вычисляйте его на этапе компиляции.

Продолжите этот урок на YourAIZone

В полном уроке — объяснение целиком, разобранные примеры, частые ошибки, тесты с мгновенной проверкой и ИИ-репетитор. Начните сразу как гость или создайте аккаунт, чтобы сохранять прогресс.

В полном уроке

  • Объяснение целиком
  • Разобранные примеры
  • Частые ошибки и как их избежать
  • Тестовые вопросы: 15
  • ИИ-репетитор, который отвечает на вопросы по теме
  • Отслеживание прогресса для ученика, родителей и учителя