cpp core · advanced

Templates: Variadic Templates, Fold Expressions, Parameter Pack Patterns

Variadic templates accept zero or more template arguments. Syntax: template<typename First, typename... Rest>. sizeof...(Args): compile-time count. Recursive pattern: handle first, recurse on Rest... — needs base case. C++17 fold expressions eliminate recursion: (0 + ... + args) = binary left fold (sum); ((cout<<args<<'\n'), ...) = comma fold (print all); (... && preds) = all-of fold with short-circuit. Use binary fold (with init) for +, * to handle empty pack safely. && || and comma are safe with empty packs (defined behavior). Pack expansion patterns: f(args...), {args...}, T(forward<Args>(args)...), tuple<Args...>. Real use: emplace_back(Args&&... args) + forward<Args>(args)... constructs T in-place. Type list ops: contains = (is_same_v<T,Pack>||...), count = (0+...+(int)is_same_v<T,Pack>).

🔑 Key line

Variadic templates: typename...Args; expand with args...; sizeof...(Args) = count; C++17 fold: (0+...+args) left fold, (args+...) right fold; perfect forward with forward<Args>(args)... — used in emplace_back, make_unique.

The code

// Variadic templates: accept any number of template arguments
// sizeof...(Args) gives count
// Base case (empty pack):
void print() {}
// Recursive variadic:
template <typename First, typename... Rest>
void print(First first, Rest... rest) {
cout << first << '\n';
print(rest...); // recursive expansion
}
print(1, 2.5, "hello"); // calls print<int,double,char*>
// C++17 fold expressions — cleaner, no recursion
// (init op ... op pack)
template <typename... Args>
auto sum(Args... args) {
return (0 + ... + args); // left fold: ((0+1)+2)+3
}
template <typename... Args>
void print2(Args... args) {
((cout << args << '\n'), ...); // comma fold over all args
}
// Fold expressions:
// Unary left: (... op pack) → ((a op b) op c)
// Unary right: (pack op ...) → (a op (b op c))
// Binary left: (init op ... op pack)
// Binary right: (pack op ... op init)
// Perfect forwarding with variadic:
template <typename T, typename... Args>
unique_ptr<T> make(Args&&... args) {
return unique_ptr<T>(new T(forward<Args>(args)...));
}
auto p = make<MyClass>(1, 2.5, "hello"); // constructs MyClass(1, 2.5, "hello")
// Parameter pack expansion patterns:
sizeof...(Args) // number of args in pack
vector<int> v = {args...}; // expand all into initializer list
tuple<Args...> t(args...); // build tuple from pack
int arr[] = {(transform(args))...}; // apply transform to each

What this lesson walks through

  1. 01Variadic templates — template<typename... Args>
  2. 02C++17 Fold expressions — eliminating recursion
  3. 03Parameter pack expansion patterns
  4. 04Variadic in real code — std::make_unique, emplace_back
  5. 05Compile-time tricks with packs — type lists
  6. 06Fold expression pitfalls and best practices

Variadic templates accept zero or more template arguments. The parameter pack Args can be any types. sizeof...(Args) gives the count at compile time. The pack is expanded with args... — the compiler recursively instantiates one function per invocation depth. This is how std::tuple, std::make_tuple, std::bind, and std::forward are implemented.

See it animated — step by step, at your own pace

Unlock the full interactive walkthrough of Templates: Variadic Templates, Fold Expressions, Parameter Pack Patterns and 100+ animated C++ interview lessons.

← Previous
Templates: SFINAE, enable_if, Type Traits, if constexpr, Concepts
Next →
Templates: Full Specialization, Partial Specialization, Explicit Instantiation