Variadic Templates and Fold Expressions
Templates and Generic Programming

5.5 Variadic Templates and Fold Expressions

Standard templates are powerful but restrict you to a fixed number of type parameters. C++11 introduced variadic templates, enabling functions and classes to accept an arbitrary number of arguments of any type. C++17 added fold expressions, allowing us to unpack these arguments statically with zero runtime loop overhead.

Let's look at parameter packs, fold operators, and compile time code expansions.

Variadic Parameter Packs

A variadic template uses the ellipsis ... operator to represent a parameter pack (a list of zero or more template arguments). You can declare a parameter pack for types, and matching parameter packs for function arguments:

template <typename... Args>
void logMessage(Args... args) {
    // sizeof... queries the number of arguments in the pack at compile time
    std::cout << "Arguments count: " << sizeof...(args) << '\n';
}
Declaring a variadic template function.

Unpacking Packs: C++17 Fold Expressions

The AST cost of recursive instantiation (C++11)

In C++11, if you wanted to sum 10 variadic arguments, you had to write a recursive template. Summing 10 arguments forced the compiler to instantiate 10 separate nested function classes in its internal AST, eating massive amounts of compiler memory.

C++17 introduced Fold Expressions, which unpack parameter packs directly using binary operators without instantiating any recursive classes. It instantly flattens the code, saving your compiler from memory exhaustion.

#include <iostream>

// Unpacks and sums all arguments using fold expression (... + args)
template <typename... T>
auto sumValues(T... args) {
    return (... + args); // Expands to: arg1 + arg2 + arg3...
}

// Writing a type safe printing function using fold expressions
template <typename... Args>
void printAll(Args... args) {
    // Fold over the comma operator: prints each argument sequentially
    (..., (std::cout << args << ' '));
    std::cout << '\n';
}

int main() {
    std::cout << "Sum: " << sumValues(1, 2.5, 3, 4.5) << '\n'; // Sums ints and doubles
    printAll("User:", "Alice", "ID:", 101, "Active:", true);
    return 0;
}
Implementing a type safe sum with fold expressions.

Compile Time Expansion

Under the hood, the compiler expands the fold expression inline. If you call sumValues(1, 2, 3), the compiler generates the code equivalent of 1 + 2 + 3 inside your compiled binary. At runtime there is no parameter-pack loop or recursive template instantiation, just straight-line arithmetic in the generated code.

* The Accordion Metaphor: Fold expressions are like playing an accordion. The template parameters start as a long, expanded list of variable arguments. When you write the fold expression, the compiler presses the accordion keys, folding the arguments down into a single execution line with zero runtime performance cost.

Chapter 5 Knowledge CheckQuestion 1 of 3

What is a main systems trade off of C++ template monomorphization (static instantiation)?

Finished reading this lesson?