· 2 min read ·

C++26 Finally Gives Tuple Iteration a Real Syntax

Source: isocpp

Iterating over a std::tuple in C++ has always required workarounds. The standard library gives you a heterogeneous container, but it never gave you a clean way to walk through it. You end up reaching for std::apply, or constructing an index_sequence and unpacking it with a fold expression, or writing recursive template specializations. These approaches work, but they carry cognitive overhead that scales poorly when you need to compose or debug them.

C++26 addresses this directly with two proposals that Bartlomiej Filipek covers in the final installment of his tuple-iteration series: structured binding packs (P1061) and expansion statements (P1306). The post went up in November 2025, and it serves as a useful retrospective on how each standard revision has chipped away at the problem.

Structured Binding Packs

P1061 extends the structured binding syntax to allow pack expansion. Before this, a structured binding like auto [a, b, c] = t; required you to know the tuple’s arity at the call site. With binding packs, you can write:

auto [...elems] = t;

This binds elems as a pack containing all elements of the tuple. From there, standard pack expansion syntax handles the rest, which turns many tuple-manipulation tasks into one-liners.

Expansion Statements

P1306 introduces template for, a loop construct that iterates over a compile-time range:

template for (auto elem : t) {
    process(elem);
}

The body is instantiated once per element, with elem taking the type of each tuple member in turn. This is semantically what you have always wanted when you write for (auto x : tuple) and get a compile error. The compiler handles the unrolling, and you get code that expresses intent without ceremony.

Why the Combination Matters

Binding packs and expansion statements complement each other. Expansion statements are cleaner than std::apply for cases where you need side effects per element; binding packs reduce the verbosity of pack-based manipulation that would otherwise require explicit std::get<I> calls. Together, they compose well: you can put an expansion statement inside a function template and reason about it the way you reason about a regular loop.

Bartlomiej’s series covers C++20 and C++23 approaches in earlier installments, which is worth reading if you are targeting those standards. The progression illustrates how the language has moved incrementally on this problem.

The broader point is that first-class tuple iteration is not a novel capability; it is table stakes for a type-safe heterogeneous container. The new syntax integrates with the existing pack expansion model rather than inventing separate machinery, which is the right call. If you are writing C++26 code, structured binding packs and expansion statements will show up often enough that learning them early pays off.

Was this interesting?