Pack indexing (since C++26)
指定されたインデックスの pack の要素にアクセスします。
目次 |
構文
id-expression
...[
expression
]
|
(1) | ||||||||
typedef-name
...[
expression
]
|
(2) | ||||||||
| typedef-name | - | パックを名前付けする 識別子 または simple-template-id |
| id-expression | - | パックを名前付けする id-expression |
| expression | - |
パックインデックス指定において、あるパック
P
に対して範囲
[
0
,
sizeof...
(
P
)
)
内のインデックスとして指定される、
std::
size_t
型の
変換済み定数式
I
|
説明
パックインデックスは、未展開パックの パック展開 に続いて、添字内の省略記号とインデックスで構成されます。パックインデックスには2種類あります:パックインデックス式とパックインデックス指定子。
P
を非空のパックとし、
P
0
, P
1
, ..., P
n-1
を含むものとする。
I
を有効なインデックスとすると、展開
P...[I]
のインスタンス化は、パック
P
の要素
P
I
を生成する。
パックのインデックス指定に非定数式インデックス
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]; // エラー: 'm' は定数式ではありません auto d = args...[runtime_idx()]; // エラー: 'runtime_idx()' は定数式ではありません }
テンプレートテンプレートパラメータのパックに対するインデックス指定は不可能です。
template <template <typename...> typename... Temps> using A = Temps...[0]<>; // エラー: 'Temps' はテンプレートテンプレートパラメータのパックです template <template <typename...> typename... Temps> using B = Temps<>...[0]; // エラー: 'Temps<>' はパック名を表していません // 単純テンプレートIDではありますが
パックインデックス式
id-expression
...[
expression
]
|
|||||||||
パックインデックス式は
id-expression
を表し、パック要素
P
I
の式を示します。
id-expression
は以下の宣言によって導入されるものとします:
- 定数テンプレートパラメータパック ,
- 関数パラメータパック ,
- ラムダ初期化キャプチャパック , または
- 構造化バインディングパック .
template <std::size_t I, typename... Ts> constexpr auto element_at(Ts... args) { // 関数パラメータパック宣言で導入された 'args' 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); // エラー: 範囲外 static_assert(element_at<0>() == 1); // エラー: 範囲外、空のパック template <std::size_t I, typename Tup> constexpr auto structured_binding_element_at(Tup tup) { auto [...elems] = tup; // 構造化バインディングパック宣言で導入された 'elems' 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' 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' 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]; // エラー // 代わりに 'Vals...[I]' を使用 template <std::size_t I, std::size_t... Vals> constexpr std::size_t triple_at = (Vals * 3)...[I]; // エラー // 代わりに 'Vals...[I] * 3' を使用 template <std::size_t I, typename... Args> constexpr decltype(auto) get(Args&&... args) noexcept { return std::forward<Args>(args)...[I]; // エラー // 代わりに 'std::forward<Args...[I]>(args...[I])' を使用 }
decltype
をパックインデックス式に適用することは、
decltype
をid式に適用する場合と同じです。
void f() { [](auto... args) { using T0 = decltype(args...[0]); // 'T0' は 'double' 型 using T1 = decltype((args...[0])); // 'T1' は 'double&' 型 }(3.14); }
パックインデックス指定子
typedef-name
...[
expression
]
|
|||||||||
パックインデックス指定子は
computed-type-specifier
を表し、パック要素
P
I
の型を示します。
typedef-name
は
type template parameter pack
の宣言によって導入されなければなりません。
template <typename... Ts> using last_type_t = Ts...[sizeof...(Ts) - 1]; static_assert(std::is_same_v<last_type_t<>, int>); // エラー: 範囲外 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" は暗黙的に 'std::string_view' に変換される std::same_as<std::string_view> auto a = f("Hello", type_seq<std::string_view>{}); // エラー: "Ok" は '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*...); // より明確だがより許容的: Ts...はcv voidまたは関数型を含む可能性がある void foo() { f<char, bool>(nullptr, nullptr); // 動作 #1 (C++26以前): // void 'f<char, bool>(char*, bool*)' (別名 'f<char, bool>(char[1], bool[1])') を呼び出す // 動作 #2 (C++26以降): // エラー: 'void f<char, bool>(bool)' が呼び出されたと推定されるが // 1つの引数ではなく2つの引数が指定されている g<char, bool>(nullptr, nullptr); // 'g<char, bool>(char*, bool*)' (別名 'g<char, bool>(char[1], bool[1])') を呼び出す h<char, bool>(nullptr, nullptr); // 'h<char, bool>(char*, bool*)' を呼び出す }
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_pack_indexing
|
202311L
|
(C++26) | Pack indexing |
例
#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)); }