Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 67 из 104.

Что делать, если вам нужна function, принимающая любое количество arguments любых типов? Традиционные шаблоны требуют точно указать, сколько параметров типов вам нужно. Вариативные шаблоны решают эту проблему, позволяя шаблонам принимать произвольное количество аргументов шаблона.

Синтаксис использует ... (многоточие) для создания parameter pack:

template <typename... Args>
void printAll(Args... args) {
    // Args — это пакет параметров шаблона
    // args — это пакет параметров функции
}

Для обработки arguments обычно используется рекурсия с base case. parameter pack раскрывается по одному argument за раз, пока не останется ни одного:

// Базовый случай: аргументов не осталось
void print() {
    std::cout << std::endl;
}

// Рекурсивный случай: обработать первый аргумент, рекурсивно вызвать с остальными
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Раскрыть оставшиеся аргументы
}

print(1, 3.14, "hello", 'x');  // Вывод: 1 3.14 hello x

Вы также можете использовать выражения fold (C++17) для более простых операций без явной рекурсии:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Fold-выражение: складывает все аргументы
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Вывод: 10

Вариативные шаблоны лежат в основе многих возможностей стандартной библиотеки, таких как std::make_unique, std::tuple и std::function. Они позволяют создавать типобезопасные функции, работающие с любыми сочетаниями типов и количеством аргументов.

challenge icon

Задание

Легко

Давайте создадим гибкую систему ведения журналов, использующую вариативные шаблоны для обработки сообщений с любым количеством аргументов. Вы создадите утилиты, которые могут объединять значения, подсчитывать аргументы и выводить отформатированный результат — всё с использованием наборов параметров.

Вы организуете код в двух файлах:

  • Variadic.h: определите здесь свои вариативные шаблонные функции.

    Создайте функцию printAll, которая выводит все свои аргументы, разделяя их пробелами, а затем переводит строку. Используйте рекурсивный подход с базовым случаем, который выводит только перевод строки, и рекурсивным случаем, который выводит первый аргумент, пробел, а затем рекурсивно обрабатывает оставшиеся аргументы.

    Создайте функцию countArgs, которая возвращает количество переданных ей аргументов. Используйте sizeof..., чтобы получить размер набора параметров.

    Создайте функцию sum, которая складывает все числовые аргументы и возвращает результат. Используйте свёрточное выражение с оператором +.

    Создайте функцию product, которая перемножает все числовые аргументы и возвращает результат. Используйте свёрточное выражение с оператором *.

  • main.cpp: считайте четыре входных значения (каждое в отдельной строке):
    1. Целое число
    2. Число с плавающей точкой двойной точности
    3. Строка
    4. Символ

    Продемонстрируйте свои вариативные шаблоны:

    1. Вызовите printAll со всеми четырьмя входными значениями и выведите результат
    2. Выведите количество аргументов при вызове countArgs с целым числом, числом двойной точности и символом: Argument count: <count>
    3. Вычислите сумму целого числа, числа двойной точности и значений 10 и 5, затем выведите: Sum: <result>
    4. Вычислите произведение целого числа и значений 2 и 3, затем выведите: Product: <result>
    5. Вызовите printAll только со строкой (один аргумент)
    6. Вызовите printAll без аргументов (должен быть выведен только перевод строки)

Например, для входных значений 5, 2.5, Hello и X:

5 2.5 Hello X 
Argument count: 3
Sum: 22.5
Product: 30
Hello 

Обратите внимание, как printAll обрабатывает любое количество аргументов разных типов — от четырёх аргументов до нуля. Функции sum и product используют свёрточные выражения, чтобы элегантно объединять все значения без явной рекурсии. Ваша функция countArgs демонстрирует, как sizeof... предоставляет размер набора параметров во время компиляции.

Попробуйте сами

#include <iostream>
#include <string>
#include "Variadic.h"

using namespace std;

int main() {
    // Прочитать входные данные
    int intVal;
    double doubleVal;
    string strVal;
    char charVal;
    
    cin >> intVal;
    cin >> doubleVal;
    cin >> strVal;
    cin >> charVal;
    
    // TODO: Вызвать printAll со всеми четырьмя входными данными
    
    // TODO: Print argument count using countArgs with intVal, doubleVal, and charVal
    // Format: "Argument count: <count>"
    
    // TODO: Вычислить и вывести сумму intVal, doubleVal, 10 и 5
    // Формат: "Sum: <result>"
    
    // TODO: Вычислить и вывести произведение intVal, 2 и 3
    // Формат: "Product: <result>"
    
    // TODO: Вызвать printAll только со строкой
    
    // TODO: Вызвать printAll без аргументов
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++