C++ Variadic Templates
In this page:
template <typename... Args>
void function_name(Args... args) {
// sizeof...(args) is the number of arguments
}
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 std::cout and std::cin
#include <iostream>
// Declare a template so this works with any suitable type
template <typename... Args>
void printAll(Args... args) {
(std::cout << ... << args) << std::endl;
}
// Program execution starts in main()
int main() {
printAll(1, 2.5, "text");
// Return 0 to signal that the program finished successfully
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 std::cout and std::cin
#include <iostream>
// Declare a template so this works with any suitable type
template <typename... Args>
int countArgs(Args... args) {
// Send sizeof...(args) back to the caller
return sizeof...(args);
}
// Program execution starts in main()
int main() {
// Print to the console with cout
std::cout << countArgs(1, 2, 3, 4) << std::endl;
// Return 0 to signal that the program finished successfully
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 std::cout and std::cin
#include <iostream>
void printAll() {}
// Declare a template so this works with any suitable type
template <typename T, typename... Rest>
void printAll(T first, Rest... rest) {
// Print to the console with cout
std::cout << first << " ";
printAll(rest...);
}
// Program execution starts in main()
int main() {
printAll(1, 2, 3);
// Print to the console with cout
std::cout << std::endl;
// Return 0 to signal that the program finished successfully
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 std::cout and std::cin
#include <iostream>
// Declare a template so this works with any suitable type
template <typename... Types>
// Define a structure type named Tuple
struct Tuple {};
// Declare a template so this works with any suitable type
template <typename First, typename... Rest>
struct Tuple<First, Rest...> {
First value;
Tuple<Rest...> rest;
};
// Program execution starts in main()
int main() {
Tuple<int, double> t;
t.value = 5;
t.rest.value = 3.14;
// Print to the console with cout
std::cout << t.value << " " << t.rest.value << std::endl;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
- Forgetting the base case for recursive pack expansion, which fails to compile.
- Putting the
...in the wrong place when declaring or expanding a pack. - Confusing
sizeof...(args)withsizeof(args).
- Templates let you write code that works with different types.
- Function templates and class templates generate type-specific code as needed.
- Template specialization and variadic templates handle special cases and variable numbers of arguments.
Chapter Quiz — Complete all 5 topics to unlock
0/5 topics done
Complete these topics first: