From cppreference.com
Accesses the element of a
at a specified index.
Syntax
id-expression...[expression](1) typedef-name...[expression](2) 1) Pack indexing expression
2) Pack indexing specifier
Explanation
Pack indexing is a pack expansion of the unexpanded pack followed by an ellipsis and index inside the subscript. There are two kinds of pack indexing: pack indexing expression and pack indexing specifier.
Let P be a non-empty pack containing P0, P1, ..., Pn-1 and I be a valid index, the instantiation of the expansion P...[I] yields the pack element PI of P.
Indexing a pack with non-constant expression index I is not allowed.
intruntime_idx();voidbar(auto...args){autoa=args...[0];constintn=1;autob=args...[n];intm=2;autoc=args...[m];// error: 'm' is not a constant expressionautod=args...[runtime_idx()];// error: 'runtime_idx()' is not a constant expression}Indexing a pack of template template parameters is not possible.
template<template<typename...>typename...Temps>usingA=Temps...[0]<>;// error: 'Temps' is a pack of template template parameterstemplate<template<typename...>typename...Temps>usingB=Temps<>...[0];// error: 'Temps<>' doesn't denote pack name // although it is a simple-template-idPack indexing expression
id-expression...[expression]Pack indexing expression denotes the id-expression, the expression of pack element PI. The id-expression shall be introduced by the declaration of:
non-type template parameter pack
,
,
, or
.
template<std::size_tI,typename...Ts>constexprautoelement_at(Ts...args){// 'args' introduced in function parameter pack declarationreturnargs...[I];}static_assert(element_at<0>(3,5,9)==3);static_assert(element_at<2>(3,5,9)==9);static_assert(element_at<3>(3,5,9)==4);// error: out of boundsstatic_assert(element_at<0>()==1);// error: out of bounds, empty packtemplate<std::size_tI,typenameTup>constexprautostructured_binding_element_at(Tuptup){auto[...elems]=tup;// 'elems' introduced in structured binding pack declarationreturnelems...[I];}structA{boola;intb;};static_assert(structured_binding_element_at<0>(A{true,4})==true);static_assert(structured_binding_element_at<1>(A{true,4})==4);// 'Vals' introduced in non-type template parameter pack declarationtemplate<std::size_tI,std::size_t...Vals>constexprstd::size_tdouble_at=Vals...[I]*2;// OKtemplate<std::size_tI,typename...Args>constexprautofoo(Args...args){return[...members=args](Args...[I]op){// 'members' introduced in lambda init-capture packreturnmembers...[I]+op;};}static_assert(foo<0>(4,"Hello",true)(5)==9);static_assert(foo<1>(3,std::string("C++"))("26")=="C++26");Indexing pack of complex expressions other than id-expression is not allowed.
template<std::size_tI,auto...Vals>constexprautoidentity_at=(Vals)...[I];// error// use 'Vals...[I]' insteadtemplate<std::size_tI,std::size_t...Vals>constexprstd::size_ttriple_at=(Vals*3)...[I];// error// use 'Vals...[I] * 3' insteadtemplate<std::size_tI,typename...Args>constexprdecltype(auto)get(Args&&...args)noexcept{returnstd::forward<Args>(args)...[I];// error// use 'std::forward<Args...[I]>(args...[I])' instead}Applying
to pack indexing expression is the same as applying
to id-expression.
voidf(){[](auto...args){usingT0=decltype(args...[0]);// 'T0' is 'double'usingT1=decltype((args...[0]));// 'T1' is 'double&'}(3.14);}Pack indexing specifier
typedef-name...[expression]Pack indexing specifier denotes the computed-type-specifier, the type of pack element PI. The typedef-name shall be introduced by the declaration of
.
template<typename...Ts>usinglast_type_t=Ts...[sizeof...(Ts)-1];static_assert(std::is_same_v<last_type_t<>,int>);// error: out of boundsstatic_assert(std::is_same_v<last_type_t<int>,int>);static_assert(std::is_same_v<last_type_t<bool,char>,char>);static_assert(std::is_same_v<last_type_t<float,int,bool*>,bool*>);Pack indexing specifier can appear as:
a
,
a
,
a
, or
the
type of an explicit destructor call
.
Pack indexing specifier can be used in function or constructor parameter list to establish
in template argument deduction.
template<typename...>structtype_seq{};template<typename...Ts>autof(Ts...[0]arg,type_seq<Ts...>){returnarg;}// OK: "Hello" is implicitly converted to 'std::string_view'std::same_as<std::string_view>autoa=f("Hello",type_seq<std::string_view>{});// Error: "Ok" is not convertible to 'int'std::same_as<int>autob=f("Ok",type_seq<int,constchar*>{});Notes
Before C++26, Ts...[N] was a valid syntax for declaring function parameter pack of unnamed arrays of size N, where the parameter types were further adjusted to pointers. Since C++26, Ts...[1] is interpreted as a pack indexing specifier which would change the behavior below to #2. To preserve the first behavior, the function parameter pack must be named, or manually adjusted to a pack of pointer types.
template<typename...Ts>voidf(Ts...[1]);template<typename...Ts>voidg(Ts...args[1]);template<typename...Ts>voidh(Ts*...);// clearer but more permissive: Ts... can contain cv void or function typesvoidfoo(){f<char,bool>(nullptr,nullptr);// behavior #1 (before C++26):// calls void ‘f<char, bool>(char*, bool*)’ (aka ‘f<char, bool>(char[1], bool[1])’)// behavior #2 (since C++26): // error: supposedly called ‘void f<char, bool>(bool)’// but provided with 2 arguments instead of 1g<char,bool>(nullptr,nullptr);// calls ‘g<char, bool>(char*, bool*)’ (aka ‘g<char, bool>(char[1], bool[1])’)h<char,bool>(nullptr,nullptr);// calls ‘h<char, bool>(char*, bool*)’}Feature-test macro ValueStdFeature
(C++26)Pack indexing
(C++29)Pack indexing for templates Example
Run this code
#include<tuple>template<std::size_t...Indices,typenameDecomposable>constexprautosplice(Decomposabled){auto[...elems]=d;returnstd::make_tuple(elems...[Indices]...);}structPoint{intx;inty;intz;};intmain(){constexprPointp{.x=1,.y=4,.z=3};static_assert(splice<2,1,0>(p)==std::make_tuple(3,4,1));static_assert(splice<1,1,0,0>(p)==std::make_tuple(4,4,1,1));}