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>).
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 listtuple<Args...> t(args...); // build tuple from packint arr[] = {(transform(args))...}; // apply transform to eachWhat this lesson walks through
- 01Variadic templates — template<typename... Args>
- 02C++17 Fold expressions — eliminating recursion
- 03Parameter pack expansion patterns
- 04Variadic in real code — std::make_unique, emplace_back
- 05Compile-time tricks with packs — type lists
- 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.