C++26 pack indexing removes recursive element selection
Kona adopts direct indexing for type and expression packs, with constant bounds and deduction rules that keep the new operation distinct from runtime subscripting.
Selecting one element from a parameter pack has often required more machinery than the selection itself. A recursive metafunction discards preceding types. A value helper forwards arguments through another sequence of instantiations. C++26 now has adopted syntax for naming the required pack element directly.
Herb Sutter's November 11 Kona report identifies P2662R3 among the core-language papers adopted at the meeting. Corentin Jabot and Pablo Halpern's proposal supplies indexing for packs of types and expressions. The operation enters the C++26 working draft, which is distinct from availability in an installed compiler.
Templates remain useful to me when their runtime abstraction cost is low. Their implementation complexity and compiler workload are another part of that tradeoff. Pack indexing interests me because a basic sequence operation no longer needs to be expressed indirectly through a library algorithm.
A pack is a sequence without ordinary subscripting
A parameter pack represents a sequence of types or expressions. Existing expansion syntax applies a pattern across that sequence. It does not, by itself, provide the ordinary operation of selecting the element at a particular position.
The proposal uses the pack's name followed by an ellipsis and a bracketed constant index. For a type pack, the result denotes a type. For an expression pack, it denotes the selected expression. The shared form makes the underlying selection recognizable in either context.
A small example under the adopted draft syntax combines the first and last values:
template<class... T>
constexpr auto first_plus_last(T... values) -> T...[0]
{
return T...[0](values...[0] + values...[sizeof...(values) - 1]);
}
static_assert(first_plus_last(1, 2, 10) == 11);
The return type selects the first type. The body selects two function parameters and converts their sum to that return type. This example follows the proposal's mechanism and assumes a compiler implementing draft C++26 pack indexing.
The selected type is not automatically a common type for every argument. The example explicitly chooses the first type and uses it for the result. A library needing a different arithmetic policy must state that policy separately.
This distinction is important because indexing only answers which element is selected. It does not decide whether adding the selected values is meaningful or whether converting the result preserves information. Those remain ordinary type and expression questions around the operation.
The index must be known at compile time
P2662R3 requires an appropriate constant expression for the index, with a value within the pack's bounds. An empty pack cannot be indexed. This is not a runtime container interface with a dynamic index.
That requirement follows the kind of result the operation can produce. A type selection must have a determinate type during compilation. Expression packs can also contain elements of different types, so a runtime selector would need another model for representing its result.
The proposal does not adopt negative indexing as shorthand for selection from the end. It discusses a search operation returning minus one on failure as a reason for caution. Treating that value as the final element could silently turn a failed search into a valid but wrong selection.
Selection from the end remains expressible by computing a valid index from the pack size, as in the example. The calculation still needs a nonempty pack and must remain within bounds. The language does not reinterpret an invalid negative result as a successful selection.
For me, this is a useful limit. An index computation should expose a failed precondition instead of supplying a different element through a shorthand the programmer may not have intended. A simple indexing facility benefits from an equally explicit domain.
Recursive helpers create repeated compiler work
The proposal compares direct syntax with library-only approaches. A recursive type helper can remove one leading element per instantiation until it reaches the requested index. A value helper can similarly recurse while forwarding the remaining arguments.
Those techniques can be implemented without new syntax, but their implementation structure becomes part of compilation. The paper explains that selecting different indices can repeat work. Retrieving many elements through independent recursive selections can therefore require many helper instantiations.
Compiler intrinsics can improve a library's implementation, and several compilers already provide such support. That is useful implementation practice, but it does not give users a uniform source-language operation. A direct language facility states the requested selection without exposing the helper's recursive structure.
This should not be turned into an invented compile-time benchmark. The proposal gives a structural argument about instantiations and discusses existing implementation techniques. The actual effect on a project depends on which helpers it currently uses and how its compiler implements them.
The source-code improvement is easier to identify directly. A type selection can name the pack and index where it is needed. The reader need not open a separate metafunction to establish that its purpose is merely selecting one element.
The syntax distinguishes packs from arrays inside packs
Using brackets directly on a pack would conflict with operations on its elements. A pack can contain arrays, and expanding an array subscript across a pack is different from indexing the pack to obtain one array.
P2662R3 analyzes these cases when comparing syntax choices. The ellipsis before the bracket identifies the pack operation. An ellipsis after a larger expression can instead describe expansion of that expression over pack elements.
That distinction matters for nested operations. Generic code may need to select an array from a pack, index every array, or use a pack of indices against another sequence. Reusing familiar symbols still requires a grammar that keeps those operations distinguishable.
The paper also considers a dot-based syntax, library functions, reserved identifiers, and pack objects. Its choice leaves other language operations room to evolve while providing one form usable for types and expressions. The final spelling is the result of those constraints, not merely the availability of an unused punctuation sequence.
Indexing does not deduce an entire pack
The proposal treats pack indexing as a non-deduced context. A function parameter written using the first type of a pack does not let the compiler infer every missing type in that pack from one argument.
This is a practical boundary around deduction. Knowing one element does not establish the sequence's length or the types at its other positions. The paper discusses the complexity of sparse deductions and chooses not to introduce that model.
A pack can instead be deduced from another parameter that exposes the complete sequence. Once the pack is known, an indexed parameter has the selected type and can participate in ordinary argument conversion. That keeps selection separate from constructing the sequence it selects from.
The choice also affects compatibility with possible future extensions. Making a context deducible later can change overload resolution. P2662R3 records why the deduction decision is part of the current design rather than an incidental implementation omission.
The wording also records a compatibility effect for unnamed function parameter packs using array syntax. A token sequence previously interpreted as that declaration can now denote pack indexing. Naming the parameter retains a way to express the array declaration. This is a reminder that the syntax reuses existing tokens within a mature grammar, so even a small addition needs an account of previously valid source.
Expression categories remain meaningful
The paper intends an indexed expression to behave like the underlying selected expression. Its treatment of decltype preserves the familiar distinction between naming an entity and examining a parenthesized expression.
That matters for forwarding code. Selecting a function parameter does not by itself transfer ownership or promise a move. The library author still needs the correct reference and value-category treatment for the operation being performed.
Pack indexing therefore removes selection machinery while retaining ordinary C++ expression obligations. This is a useful balance for generic libraries. A new operation need not invent a second lifetime or ownership model merely to name an existing argument.
Draft adoption does not settle every related feature
The Kona report distinguishes adopted work from progress in other subgroups. Pack indexing has entered the working draft. Contracts and reflection have made progress toward C++26 but occupy different stages in that report.
That distinction is particularly relevant because pack operations connect naturally to reflection and future ways of introducing packs. The proposal discusses future directions, but those discussions do not make every related syntax part of the adopted feature.
The immediate facility has a clear boundary. A known pack and valid constant index produce a selected type or expression. Library code can replace recursive element selection with that direct operation once its compiler supplies the C++26 implementation, while preserving its existing decisions about conversions, ownership, and the selected expression's use.