包索引 (自 C++26 起)
来自 cppreference.com
访问参数包中指定索引处的元素。
内容 |
[编辑] 语法
id-expression ...[ expression ] |
(1) | ||||||||
typedef-name ...[ expression ] |
(2) | ||||||||
1) 包索引表达式
2) 包索引说明符
typedef-name | - | 一个标识符或一个简单模板标识符,它命名参数包 |
id-expression | - | 一个id-expression,它命名参数包 |
expression | - | 一个已转换常量表达式 I,类型为std::size_t,指定为索引,其中I在范围内[ 0, sizeof...(P)) ,对于包索引中的某个包P |
[编辑] 解释
包索引是未扩展参数包的包扩展,后面跟着省略号和下标内的索引。包索引有两种:包索引表达式和包索引说明符。
令P
为一个包含P0, P1, ..., Pn-1
的非空参数包,I
为有效索引,扩展P...[I]
的实例化将产生P
的包元素PI
。
不允许使用非常量表达式索引对包进行索引。
int runtime_idx(); void bar(auto... args) { auto a = args...[0]; const int n = 1; auto b = args...[n]; int m = 2; auto c = args...[m]; // error: 'm' is not a constant expression auto d = args...[runtime_idx()]; // error: 'runtime_idx()' is not a constant expression }
无法对模板模板参数包进行索引。
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
[编辑] 包索引表达式
id-expression ...[ expression ] |
|||||||||
包索引表达式表示id-expression,即包元素PI
的表达式。id-expression
应由以下内容的声明引入:
template <std::size_t I, typename... Ts> constexpr auto element_at(Ts... args) { // 'args' introduced in function parameter pack declaration return 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 bounds static_assert(element_at<0>() == 1); // error: out of bounds, empty pack // 'Vals' introduced in non-type template parameter pack declaration template <std::size_t I, std::size_t... Vals> constexpr std::size_t double_at = Vals...[I] * 2; // OK template <std::size_t I, typename... Args> constexpr auto foo(Args... args) { return [...members = args](Args...[I] op) { // 'members' introduced in lambda init-capture pack return members...[I] + op; }; } static_assert(foo<0>(4, "Hello", true)(5) == 9); static_assert(foo<1>(3, std::string("C++"))("26") == "C++26");
不允许对除 id-expression 之外的复杂表达式的包进行索引。
template <std::size_t I, auto... Vals> constexpr auto identity_at = (Vals)...[I]; // error // use 'Vals...[I]' instead template <std::size_t I, std::size_t... Vals> constexpr std::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 { return std::forward<Args>(args)...[I]; // error // use 'std::forward<Args...[I]>(args...[I])' instead }
将decltype
应用于包索引表达式与将decltype
应用于 id-expression 相同。
void f() { [](auto... args) { using T0 = decltype(args...[0]); // 'T0' is 'double' using T1 = decltype((args...[0])); // 'T1' is 'double&' }(3.14); }
[编辑] 包索引说明符
typedef-name ...[ expression ] |
|||||||||
包索引说明符表示计算类型说明符,即包元素PI
的类型。typedef-name
应由类型模板参数包的声明引入。
template <typename... Ts> using last_type_t = Ts...[sizeof...(Ts) - 1]; static_assert(std::is_same_v<last_type_t<>, int>); // error: out of bounds static_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*>);
包索引说明符可以出现在以下位置:
- 一个简单类型说明符,
- 一个基类说明符,
- 一个嵌套名称说明符,或
- 一个显式析构函数调用的类型。
包索引说明符可以在函数或构造函数参数列表中使用,以在模板参数推导中建立非推导上下文。
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, const char*>{});
[编辑] 注释
在 C++26 之前,Ts...[N]
是用于声明大小为N
的未命名数组的函数参数包的有效语法,其中参数类型进一步调整为指针。自 C++26 起,Ts...[1]
被解释为一个包索引说明符,它会将下面的行为更改为 #2。为了保留第一种行为,函数参数包必须命名,或者手动调整为指针类型包。
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*)' }
特性测试宏 | 值 | Std | 特性 |
---|---|---|---|
__cpp_pack_indexing |
202311L | (C++26) | 包索引 |
[编辑] 示例
本节内容不完整 原因:示例 |