C++ Variadic Templates
In this page:
What are Variadic Templates?
Variadic templates accept a variable number of arguments of arbitrary, potentially different types, giving you a type-safe, compile-time-checked alternative to old C-style variable arguments (like printf's ...).
Example: What are Variadic Templates?
#include <iostream>
template <typename... Args>
void printAll(Args... args) {
(std::cout << ... << args) << std::endl;
}
int main() {
printAll(1, 2.5, "text");
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Parameter Packs
The ellipsis symbol (...) declares what's called a parameter pack, which can stand in for any number of template type parameters or function arguments — zero, one, or many — determined entirely by how the template is actually used.
Example: Parameter Packs
#include <iostream>
template <typename... Args>
int countArgs(Args... args) {
return sizeof...(args);
}
int main() {
std::cout << countArgs(1, 2, 3, 4) << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Recursive Pack Expansion
To process the contents of a parameter pack, you typically unpack it recursively: the function handles the first argument, then calls itself again with the rest of the pack, peeling off one argument per recursive call until none remain.
Example: Recursive Pack Expansion
#include <iostream>
void printAll() {}
template <typename T, typename... Rest>
void printAll(T first, Rest... rest) {
std::cout << first << " ";
printAll(rest...);
}
int main() {
printAll(1, 2, 3);
std::cout << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Defining Base Cases
Recursive variadic templates need a base-case overload to actually stop the recursion — typically a version of the function that takes zero arguments — which the compiler selects once the parameter pack has been fully consumed.
Example: Defining Base Cases
#include <iostream>
void printAll() {
std::cout << std::endl; // base case: stops the recursion
}
template <typename T, typename... Rest>
void printAll(T first, Rest... rest) {
std::cout << first << " ";
printAll(rest...);
}
int main() {
printAll(1, 2, 3);
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Variadic Struct Nesting
Parameter packs can also be expanded inside struct definitions, which is exactly the technique the standard library uses internally to implement variable-length structures like std::tuple.
Example: Variadic Struct Nesting
#include <iostream>
template <typename... Types>
struct Tuple {};
template <typename First, typename... Rest>
struct Tuple<First, Rest...> {
First value;
Tuple<Rest...> rest;
};
int main() {
Tuple<int, double> t;
t.value = 5;
t.rest.value = 3.14;
std::cout << t.value << " " << t.rest.value << std::endl;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Chapter Quiz — Complete all 5 topics to unlock
0/5 topics done
Complete these topics first: