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

Начало урока

Вариативные шаблоны

Шаблоны, принимающие любое число аргументов любых типов

Главная идея: вариативный шаблон - это шаблон, принимающий пакет параметров: ноль или более шаблонных аргументов, объединённых под одним именем. Именно на этом построены std::make_unique, vector::emplace_back, std::tuple и типобезопасное форматирование. Всё разрешается компилятором на этапе инстанцирования, поэтому вариативный вызов стоит ровно столько же, сколько написанная вручную функция, в которую он разворачивается: без varargs, без стирания типов во время выполнения, без кучи.
К концу вы сможете:
  • объявлять шаблонные и функциональные пакеты параметров и узнавать их длину через sizeof...,
  • разворачивать пакет оператором ..., включая разворачивание шаблона выражения,
  • обрабатывать пакет рекурсией (голова / хвост) и понимать, когда нужен базовый случай,
  • сворачивать пакет в одно выражение через fold-выражения C++17,
  • идеально пробрасывать пакет через std::forward.

1. Пакеты параметров и sizeof...

Вместе работают два вида пакетов. Шаблонный пакет параметров записывается с ... после typename (или после нетипового либо шаблонного параметра); функциональный пакет параметров использует этот пакет как тип для последовательности параметров функции.

template <typename... Ts>      // Ts - ШАБЛОННЫЙ пакет параметров (список типов)
void probe(Ts... args) {       // args - ФУНКЦИОНАЛЬНЫЙ пакет параметров (список значений)
    static_assert(sizeof...(Ts) == sizeof...(args));   // обе длины совпадают
}

Пакет - это не тип и не значение. Это список времени компиляции, с которым можно сделать ровно две вещи: узнать его длину или развернуть его. Длину даёт sizeof... - это constexpr типа std::size_t, вычисляемый целиком на этапе компиляции; он считает элементы, а не суммирует их размеры:

template <typename... Ts>
void howMany(Ts... args) {
    std::cout << "arity = " << sizeof...(args) << "\n";   // например 3
}
howMany(1, 2.0, 'x');   // arity = 3 ; Ts = {int, double, char}

Пустой пакет допустим: howMany() компилируется и печатает arity = 0. Именно этот граничный случай - источник большинства ошибок в вариативном коде, поэтому держите его в голове с самого начала.

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

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

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

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