パックインデックス化 (C++26以降)
指定されたインデックスにあるパックの要素にアクセスします。
目次 |
[編集] 構文
id-expression ...[ expression ] |
(1) | ||||||||
typedef-name ...[ expression ] |
(2) | ||||||||
| typedef-name | - | パックを命名する識別子またはsimple-template-id |
| id-expression | - | パックを命名するid-expression |
| 式 | - | パックインデックス化における、とあるパック P に対して範囲 [0, sizeof...(P)) 内にある型 std::size_t の変換された定数式 I は、インデックスとして指定されます。 |
[編集] 説明
パックインデックス化は、展開されていないパックに省略符号と添字内のインデックスが続くパック展開です。パックインデックス化には、パックインデックス化式とパックインデックス化指定子の2種類があります。
P を P0, P1, ..., Pn-1 を含む空でないパックとし、I を有効なインデックスとすると、展開 P...[I] のインスタンス化は P のパック要素 PI を生成します。
非定数式インデックス I でパックをインデックス化することは許可されていません。
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 template <std::size_t I, typename Tup> constexpr auto structured_binding_element_at(Tup tup) { auto [...elems] = tup; // 'elems' introduced in structured binding pack declaration return 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 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を適用することは、id-expressionにdecltypeを適用することと同じです。
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 ] |
|||||||||
パックインデックス化指定子は、computed-type-specifier、つまりパック要素 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*)' }
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_pack_indexing |
202311L |
(C++26) | パックインデックス |
[編集] 例
#include <tuple> template <std::size_t... Indices, typename Decomposable> constexpr auto splice(Decomposable d) { auto [...elems] = d; return std::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)); }