Accesses the element of apack at a specified index.
|
id-expression...[ expression] | (1) | |
|
typedef-name...[ expression] | (2) | |
|
1) Pack indexing expression
2) Pack indexing specifier
[edit]Explanation
Pack indexing is apack 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.
LetP
be a non-empty pack containingP0, P1, ..., Pn-1
andI
be a valid index, the instantiation of the expansionP...[I]
yields the pack elementPI
ofP
.
Indexing a pack with non-constant expression indexI
is not allowed.
int runtime_idx(); void bar(auto...args){auto a= args...[0];constint n=1;auto b= args...[n];int m=2;auto c= args...[m];// error: 'm' is not a constant expressionauto d= 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>using A= Temps...[0]<>;// error: 'Temps' is a pack of template template parameters template<template<typename...>typename...Temps>using B= Temps<>...[0];// error: 'Temps<>' doesn't denote pack name// although it is a simple-template-id
[edit]Pack indexing expression
|
id-expression...[ expression] | | |
|
Pack indexing expression denotes theid-expression, the expression of pack elementPI
. Theid-expression shall be introduced by the declaration of:
template<std::size_t I,typename...Ts>constexprauto element_at(Ts...args){// 'args' introduced in function parameter pack declarationreturn args...[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 pack template<std::size_t I,typename Tup>constexprauto structured_binding_element_at(Tup tup){auto[...elems]= tup;// 'elems' introduced in structured binding pack declarationreturn elems...[I];} struct A{bool a;int b;}; 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 constant template parameter pack declarationtemplate<std::size_t I,std::size_t...Vals>constexprstd::size_t double_at= Vals...[I]*2;// OK template<std::size_t I,typename...Args>constexprauto foo(Args...args){return[...members= args](Args...[I] op){// 'members' introduced in lambda init-capture packreturn members...[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_t I,auto...Vals>constexprauto identity_at=(Vals)...[I];// error// use 'Vals...[I]' instead template<std::size_t I,std::size_t...Vals>constexprstd::size_t triple_at=(Vals*3)...[I];// error// use 'Vals...[I] * 3' instead template<std::size_t I,typename...Args>constexpr decltype(auto) get(Args&&...args)noexcept{returnstd::forward<Args>(args)...[I];// error// use 'std::forward<Args...[I]>(args...[I])' instead}
Applyingdecltype
to pack indexing expression is the same as applyingdecltype
to id-expression.
void f(){[](auto...args){using T0= decltype(args...[0]);// 'T0' is 'double'using T1= decltype((args...[0]));// 'T1' is 'double&'}(3.14);}
[edit]Pack indexing specifier
|
typedef-name...[ expression] | | |
|
Pack indexing specifier denotes thecomputed-type-specifier, the type of pack elementPI
. Thetypedef-name shall be introduced by the declaration oftype template parameter pack.
template<typename...Ts>using last_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:
Pack indexing specifier can be used in function or constructor parameter list to establishnon-deduced contexts in template argument deduction.
template<typename...>struct type_seq{}; template<typename...Ts>auto f(Ts...[0] arg, type_seq<Ts...>){return arg;} // OK: "Hello" is implicitly converted to 'std::string_view'std::same_as<std::string_view>auto a= f("Hello", type_seq<std::string_view>{}); // Error: "Ok" is not convertible to 'int'std::same_as<int>auto b= f("Ok", type_seq<int,constchar*>{});
Before C++26,Ts...[N] was a valid syntax for declaring function parameter pack of unnamed arrays of sizeN, 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>void f(Ts...[1]); template<typename...Ts>void g(Ts...args[1]); template<typename...Ts>void h(Ts*...);// clearer but more permissive: Ts... can contain cv void or function types void foo(){ 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 1 g<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*)'}
[edit]Example
#include <tuple> template<std::size_t...Indices,typename Decomposable>constexprauto splice(Decomposable d){auto[...elems]= d;returnstd::make_tuple(elems...[Indices]...);} struct Point{int x;int y;int z;}; int main(){constexpr Point p{ .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));}