C++ Variadic Templates & C++26 Pack Indexing: Parameter Packs, Fold Expressions & Type Lists

C++ Variadic Templates & C++26 Pack Indexing: Parameter Packs, Fold Expressions & Type Lists
Table of Contents
- Parameter Pack Fundamentals
- Pack Expansion: Where the ... Goes
- sizeof...: Pack Size at Compile Time
- Recursive Variadic Processing (Pre-C++17)
- Fold Expressions (C++17): All Four Forms
- Practical Fold Patterns
- C++26 Pack Indexing: Direct Access by Index
- Compile-Time Type Lists with Variadic Templates
- std::tuple and std::apply: Storing Packs
- Frequently Asked Questions
- Key Takeaway
Parameter Pack Fundamentals
A parameter pack is a sequence of zero or more types (or values) bundled under a single name:
Pack Expansion: Where the ... Goes
The ... after an expression expands the pack, applying the pattern to each element:
sizeof...: Pack Size at Compile Time
Recursive Variadic Processing (Pre-C++17)
Before fold expressions, recursion was the only way to iterate a pack:
Fold Expressions (C++17): All Four Forms
Fold expressions collapse a pack with a binary operator — no recursion needed:
Practical Fold Patterns
C++26 Pack Indexing: Direct Access by Index
C++26 introduces ...[N] syntax for direct element access — no more recursive unwrapping:
std::tuple and std::apply: Storing Packs
std::tuple is the standard way to store a variadic pack for later use:
Frequently Asked Questions
Can a variadic template have a mix of type and non-type packs?
Not directly — a single ... introduces either a type pack (typename...) or a non-type pack (auto... or a specific type + ...). However, you can have both in one template: template<typename... Types, auto... Values> — though the two packs must be deducible separately.
Is there a compile-time performance cost to large packs?
Yes — each unique combination of template arguments generates a separate instantiation. A function called with 10 different argument count variations generates 10 specializations. Very large packs (>50 elements) can significantly slow compilation. For high-count cases, prefer std::initializer_list<T> (if all types are the same), or encode the pack in a std::tuple and pass the tuple instead.
What is the difference between expanding pack with ... before vs after?
The position of ... specifies what gets expanded: Args... expands the types, args... expands the values, and (pattern(args))... expands the pattern applied to each value. Specifically: f(args...) passes all args to one call, while (f(args), ...) calls f once for each arg (fold over comma operator).
Key Takeaway
Variadic templates are the backbone of C++'s most powerful generic utilities: printf-style formatting, std::tuple, std::variant, std::make_unique, and event systems all use them. C++17 fold expressions eliminated the need for recursive base cases for most operations. C++26 pack indexing closes the last gap — you can now randomly access pack elements by index as naturally as array access, making complex generic code readable and maintainable.
Read next: SIMD & Low-Level Optimization →
Part of the C++ Mastery Course — 30 modules from modern C++ basics to expert systems engineering.
