5.2.16 · D1C++ Programming

Foundations — Variadic templates — parameter packs, fold expressions

2,100 words10 min readBack to topic

Before you can read any line of the parent note, you must own every symbol it throws at you. This page lists them in build-order: each new symbol only uses ones already defined above it. Nothing is assumed.


0. The very first picture: what "the pile" looks like

When you call an ordinary function you hand it a fixed number of values:

add(3, 4)   // exactly two things

A variadic function instead accepts a pile of values — 0, 1, 2, 5, a hundred — and we draw that pile as boxes sitting in a row.

Look at the figure. The blue boxes are the individual values you passed. The yellow bracket underneath, labelled args, is the name for the whole pile at once. That single yellow name is the thing the rest of this topic manipulates. Keep this picture in your head: one name, many boxes.


1. template — WHY it comes first

Before variadics, you need the idea of a template.

template<typename T>   // T is the blank
T bigger(T a, T b) { return a > b ? a : b; }

Why the topic needs it: a parameter pack is a template blank that holds many types instead of one. You cannot understand "pack" without first understanding "one blank". See Templates and Generics and Function Templates for the full story of the stamp.

Reading guide for the pieces:

  • template<...> — "here comes a recipe with blanks".
  • typename T — "the blank is a type, and I'll call it T".
  • T a, T b — ordinary parameters that happen to use the blank.

2. typename — WHAT the blank holds

Why we care: in template<typename... Ts> (the parent's very first formula) the word typename tells you the pile Ts is a pile of types, not a pile of values. Two different piles will appear (types and values) and this keyword marks the type one.


3. The ellipsis ... — the single most important symbol

Everything hard about this topic is one symbol used in two directions. We take it slowly with a figure.

Study the two halves of the figure.

Left half (declare): the dots are on the left of the name Ts. This is the machine's intake — it scoops many separate things into one pile named Ts. That is why the parent writes typename... Ts.

Right half (expand): the dots are on the right of a small pattern. Here the machine unpacks the pile, repeating the pattern once per box. So args... becomes a0, a1, a2, and the pattern f(args)... becomes f(a0), f(a1), f(a2) — the whole shape to the left of the dots is copied for each box.

Why the topic needs it: without ... you'd have to name each box (a0, a1, ...) by hand, but you don't know how many boxes exist. The ellipsis is the "for every box" instruction that makes an unknown-length pile writable in one line.


4. Two kinds of pack — types vs values

The parent uses two piles that look almost identical. Separate them now.

They come as a matched pair: each value in args has a type in Ts, box for box.

template<typename... Ts>   // Ts  = pile of TYPES
void f(Ts... args);        // args = pile of VALUES (one per type)

5. sizeof... — HOW MANY boxes (not how many bytes)

You'll meet the two words attached to sizeof...:

So sizeof...(Ts) is "a compile-time count, of type size_t". Nothing more mysterious than that.


6. Binary operators and association — the ground under folds

A fold expression (the parent's Section 3) collapses a pile using a binary operator. You must own two words first.

Now the subtle one — which pair goes first when you chain them.

The figure shows the same three numbers 1, 2, 3 folded two ways.

  • Left fold (top, blue): squeeze the leftmost pair first → (1-2)-3 = -4.
  • Right fold (bottom, yellow): squeeze the rightmost pair first → 1-(2-3) = 2.

Same numbers, same operator, different answer — because - cares about grouping. This is exactly why the parent has four folds (left/right × unary/binary): the direction the dots sit tells you which pairing happens first.

Bridge to the parent: in a fold, the ... sits on the side that "opens up deepest". Dots on the left → left-associative. Dots on the right → right-associative. That single rule decodes all four folds.


7. Recursion — the old engine before folds

The parent's Section 2 processes a pile without folds, using recursion.

This matters because it explains why fold expressions were invented: recursion needs two functions (the peeler and the empty-pile stopper) to express one idea, and folds do it in a single line.


8. Two more names you'll bump into

You don't need to master these to read the parent — just recognise the names.


The prerequisite map

templates - code with blanks

typename - the blank is a type

ellipsis - do this for every box

type pack Ts

value pack args

sizeof... - how many boxes

binary operators

association - which pair first

fold expressions

recursion + base case

old-style pack processing

Variadic Templates

Read it top to bottom: templates give you a blank, typename says the blank is a type, the ellipsis lets one blank hold many things, and the two branches (folds vs recursion) are the two ways to use the pile.


Equipment checklist

Cover the right side and answer aloud. If any stumps you, re-read its section above.

A template in one phrase
A recipe for code with a type-blank the compiler fills in.
What typename reserves a spot for
The name of a type (int, string, ...), not a value.
The two jobs of ... and how to tell them apart
Before a name = declare a pack; after a pattern = expand it. Look at which side the dots are on.
Difference between the pack Ts and the pack args
Ts is a pile of types; args is the matching pile of values.
What sizeof...(Ts) returns
The count of elements — a compile-time size_t, NOT a byte size.
Why sizeof...sizeof
sizeof = bytes of one object; sizeof... = number of pack elements.
What "left-associative" means for a - b - c
Group from the left: (a - b) - c.
Why fold direction matters for - but not +
- gives different answers under different grouping; + is order-independent.
What a base case does in recursion
Stops the self-calls when the pile is empty, so recursion terminates.
Why fold expressions replaced recursion for many tasks
Recursion needs two functions for one idea; a fold does it in one line.