Variadic template
В програмуванні, variadic templates — шаблони з змінною кількістю аргументів.
Variadic templates підтримуються мовою C++ (починаючи з стандарту C++11), та мовою програмування D.
Синтаксис variadic template в мові C++11 в загальному випадку наступний:
template<typename... Values> class tuple; // нуль або більше аргументів
Якщо потрібно заборонити використання шаблону без параметрів, то використовують наступний підхід, що вимагає використання як мінімум одного параметра:
template<typename First, typename... Rest> class tuple; // один або більше аргументів
variadic templates також можуть застосовуватися з функціями:
template<typename... Params>
void printf(const std::string &str_format, Params... parameters);
Часто використовують рекурсивний виклик variadic templates. Самі варіаційні параметри не є доступними для реалізації функції або класу. Тому для реалізації заміни звичного printf через variadic templates в C++11 типовим підходом буде наступний:
// базовий варіант
void printf(const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("неправильний формат рядка: відсутні аргументи");
}
}
std::cout << *s++;
}
}
// рекурсивний
template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cout << value;
s += 2; // зрозуміло, що це працюватиме лише з 2-х символьними специфікаторами ( %d, %f, і т.п. ); але не з %5.4f і т.п.
printf(s, args...); // буде викликатися навіть для *s рівного nullptr, але не робитиме нічого, просто ігноруватиме аргументи
return;
}
}
std::cout << *s++;
}
}
Зауважте, що побудована з застосуванням variadic template шаблонна версія printf викликає себе рекурсивно або (коли список аргументів args… буде порожній) викликає базовий варіант.
Приклад
Приклад короткої програми на мові С++ де застосовано variadic template, що підраховує суму змінного числа аргументів:
#include <iostream>
template<typename T>
T SUM(T v)
{
return v;
}
template<typename T, typename T1, typename... Ts>
T SUM(T v, T1 v1, Ts... vs)
{
return v + SUM(v1, vs...);
}
int main(int argc, char**)
{
std::cout << SUM(2, 2) << std::endl;
std::cout << SUM(argc, 1, 2, 3) << std::endl;
}
Примітки
У стандарті C++17 з'явились вирази згортання(англ. Fold expressions), що значно полегшують використання технології variadic templates. Наприклад, з їх використанням код функції SUM може мати такий вигляд:
template<typename ...Ts> //C++17
//Універсальні посилання на кожен з параметрів
auto SUM(Ts&& ... param){
return (std::forward<Ts>(param) + ... + 0); //Приймає вигляд (param1 + (param2 + (...(0)))
//Зверніть увагу: 0 виступає в ролі нейтрального значення
}
Розглянемо можливий виклик функції:
int main(int argc, char *argv[])
{
constexpr int a = 100;
std::cout<<SUM(10, 20, a); //Результат: 130
}