Variadic Templates — C++
What are Variadic Templates?
Introduced in C++11, variadic templates accept any number of type arguments. They power std::tuple, std::make_unique, std::forward, and many other standard library facilities.
Parameter Pack Syntax
template <typename... Args> // template parameter pack
void print_all(Args... args); // function parameter pack
// sizeof... gets the count
template <typename... Args>
constexpr std::size_t count() { return sizeof...(Args); }
Pack Expansion
Appending ... expands a pack in the appropriate context:
template <typename... Args>
void forward_all(Args&&... args) {
some_func(std::forward<Args>(args)...); // expands both packs
}
Recursive Variadic Templates
Classic C++11 pattern: base case + recursive case:
// Base case
void print() {}
// Recursive case
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
std::cout << first << " ";
print(rest...); // recurse with remaining args
}
// Usage: print(1, "hello", 3.14);
[!NOTE] C++17 fold expressions replace most recursive variadic templates with shorter, clearer code.
Fold Expressions (C++17)
Fold expressions collapse a pack using a binary operator in a single expression:
// Unary right fold: (args op ...)
template <typename... Args>
auto sum(Args... args) { return (args + ...); }
// Unary left fold: (... op args)
template <typename... Args>
auto sum_left(Args... args) { return (... + args); }
// Binary fold with initial value
template <typename... Args>
auto sum_with_init(Args... args) { return (0 + ... + args); }
// Print with fold
template <typename... Args>
void print_all(Args&&... args) {
((std::cout << args << " "), ...);
}
sizeof…(Args)
template <typename... Args>
void show_count(Args... args) {
std::cout << "Pack has " << sizeof...(args) << " elements\n";
}
Common Use Cases
Perfect forwarding wrapper:
template <typename T, typename... Args>
std::unique_ptr<T> make(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
[!TIP] This pattern is exactly how
std::make_uniqueandstd::make_sharedare implemented in the standard library.
Variadic Template Instantiation
flowchart TD A[“print(1, hello, 3.14)”] –> B[“Instantiate print(int, string, double)”] B –> C[“Print 1, call print(hello, 3.14)”] C –> D[“Instantiate print(string, double)”] D –> E[“Print hello, call print(3.14)”] E –> F[“Instantiate print(double)”] F –> G[“Print 3.14, call print()”] G –> H[“Base case: print() - done”]
Fold Expression Types
flowchart LR A[“Fold Expressions (C++17)”] –> B[“Unary Right Fold”] A –> C[“Unary Left Fold”] A –> D[“Binary Right Fold”] A –> E[“Binary Left Fold”] B –> B1[“(args op …)”] C –> C1[“(… op args)”] D –> D1[“(args op … op init)”] E –> E1[“(init op … op args)”]
Summary Table
| Feature | Syntax | C++ Version | Use case |
|---|---|---|---|
| Parameter pack | typename... Args | C++11 | Accept any number of types |
| Pack expansion | args... | C++11 | Expand pack in expression |
sizeof... | sizeof...(Args) | C++11 | Count pack elements |
| Recursive variadic | base + recursive fn | C++11 | Process each element |
| Unary fold | (args op ...) | C++17 | Reduce pack with operator |
| Binary fold | (init op ... op args) | C++17 | Reduce with initial value |