Как использовать variadic templates?cplus-10

Variadic templates (вариативные шаблоны) — мощная возможность C++, позволяющая работать с произвольным количеством аргументов шаблона. Вот профессиональный разбор их применения.

1. Базовый синтаксис

Объявление вариативного шаблона:

template<typename... Args>
void myFunction(Args... args) {
    // Обработка args...
}

Где ... указывает на то, что шаблон или функция принимает переменное количество аргументов.

2. Распаковка аргументов

2.1. Рекурсивная распаковка

// Базовый случай рекурсии
void process() {} 

template<typename T, typename... Args>
void process(T first, Args... rest) {
    handleSingle(first); // Обработка первого аргумента
    process(rest...);    // Рекурсивный вызов для остальных
}

2.2. Использование fold expressions

template<typename... Args>
auto sum(Args... args) {
    return (args + ...); // Распаковка с оператором +
}

3. Практические примеры

3.1. Реализация безопасного printf

void safePrint(const char* s) {
    while (*s) {
        if (*s == '%' && *(++s) != '%')
            throw std::runtime_error("invalid format string");
        std::cout << *s++;
    }
}

template<typename T, typename... Args>
void safePrint(const char* s, T value, Args... args) {
    while (*s) {
        if (*s == '%' && *(++s) != '%') {
            std::cout << value;
            return safePrint(++s, args...);
        }
        std::cout << *s++;
    }
    throw std::runtime_error("extra arguments provided");
}

3.2. Создание кортежа

template<typename... Types>
class Tuple;

template<typename Head, typename... Tail>
class Tuple<Head, Tail...> : private Tuple<Tail...> {
    Head head;
public:
    Head& getHead() { return head; }
    Tuple<Tail...>& getTail() { return *this; }
};

template<>
class Tuple<> {}; // Базовый случай

4. Работа с типами

4.1. Подсчет количества аргументов

template<typename... Args>
constexpr size_t countArgs(Args... args) {
    return sizeof...(Args); // или sizeof...(args)
}

4.2. Проверка наличия типа

template<typename T, typename... Args>
struct contains : std::false_type {};

template<typename T, typename... Args>
struct contains<T, T, Args...> : std::true_type {};

template<typename T, typename U, typename... Args>
struct contains<T, U, Args...> : contains<T, Args...> {};

5. Продвинутые техники

5.1. Perfect forwarding

template<typename... Args>
void wrapper(Args&&... args) {
    target(std::forward<Args>(args)...);
}

5.2. Создание вариативного класса

template<typename... Bases>
class VariadicDerived : public Bases... {
public:
    VariadicDerived(const Bases&... bases) : Bases(bases)... {}
};

6. Ограничения и обходные пути

6.1. Ограничение типов

template<typename... Args,
         typename = std::enable_if_t<(std::is_integral_v<Args> && ...)>>
void integralSum(Args... args) {
    // Только для целочисленных типов
}

6.2. Частичная специализация

template<typename...>
struct MyStruct;

template<typename First, typename... Rest>
struct MyStruct<First, Rest...> {
    // Специализация для одного и более аргументов
};

template<>
struct MyStruct<> {
    // Специализация для нуля аргументов
};

7. Взаимодействие с другими возможностями C++

7.1. constexpr + variadic templates

template<typename... Args>
constexpr auto allTrue(Args... args) {
    return (args && ...);
}

7.2. Лямбды и variadic templates

template<typename... Args>
void processWithLambda(Args... args) {
    auto lambda = [](auto... params) {
        // Обработка params...
    };
    lambda(args...);
}

Резюмируем

Variadic templates позволяют:

  1. Создавать функции и классы с произвольным количеством параметров
  2. Реализовывать гибкие и типобезопасные интерфейсы
  3. Выполнять сложные манипуляции с наборами типов

Ключевые техники:

  • Рекурсивная распаковка - классический подход
  • Fold expressions - компактная запись операций (C++17)
  • Perfect forwarding - эффективная передача аргументов
  • Шаблонная специализация - обработка разных случаев

Используйте variadic templates для:

  • Реализации обобщенных утилит (printf, make_unique)
  • Создания типобезопасных контейнеров (tuple, variant)
  • Построения DSL (предметно-ориентированных языков)
  • Реализации декораторов и адаптеров

Помните о балансе между гибкостью и сложностью кода. Variadic templates — мощный инструмент, но его избыточное использование может ухудшить читаемость.