Skip to content

Commit c7f3ae1

Browse files
committed
C++26: template for文を追加 (close #1494)
1 parent f665cae commit c7f3ae1

7 files changed

Lines changed: 210 additions & 4 deletions

File tree

implementation-status.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@
327327
| P1967R14: [ファイルを読み込む`#embed`命令を追加](/lang/cpp26/embed.md) | バイナリファイルをインクルードするメカニズム。`#include`とちがって読み出しサイズなどの柔軟な指定ができる | 15 | | | |
328328
| P3618R0: [`main`関数をグローバルモジュールに含められるようにする](/lang/cpp26/allow_attaching_main_to_the_global_module.md) | `main`関数に`extern "C++"`を指定できるようにすることで名前付きモジュールに含められるようにする | 16 | 21 | | | |
329329
| P2996R13: [静的リフレクション](/lang/cpp26/reflection.md.nolink) | リフレクション演算子`^^``std::meta::info`型によりコンパイル時にさまざまな情報を取得できる | | | | |
330-
| P1306R5: [コンパイル時のタプルやリストを展開処理する`template for`](/lang/cpp26/expansion_statements.md.nolink) | クラス・タプル・Range・パラメータパックなどを展開してすべての要素をコンパイル時の処理する`template for`文を追加 | 16 | | | |
330+
| P1306R5: [コンパイル時のタプルやリストを展開処理する`template for`](/lang/cpp26/expansion_statements.md) | タプル・構造体・Range・パラメータパックなどの各要素に対してコンパイル時に文を展開する`template for`文を追加 | 16 | | | |
331331
| P3533R2: [`constexpr`仮想継承を許可](/lang/cpp26/constexpr_virtual_inheritance.md) | 定数式の文脈での仮想継承を許可 | 16 | | | |
332332
| P2843R3: [プリプロセッサ仕様での「未定義動作」を不適格 (診断不要) に変更](/lang/cpp26/preprocessing_is_never_undefined.md) | プリプロセッサとレキサーの文脈での「未定義動作」用語を不適格 (診断不要) に変更 | 16 | | | |
333333
| P3868R1: [モジュール宣言より前での`#line`ディレクティブの使用を許可する](/lang/cpp26/allow_line_before_module_declarations.md) | モジュール宣言より前での`#line`ディレクティブの使用を禁止していたのは過度な制限だった | | | | |

lang/cpp17/structured_bindings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ auto { w, {x, y}, z } = f(); // このような、tuple内にあるpairを同時
342342
- [C++26 宣言のみで使用しない変数の名前として`_`をサポート](/lang/cpp26/nice_placeholder_with_no_name.md)
343343
- [C++26 構造化束縛でパックを導入できるようにする](/lang/cpp26/structured_bindings_can_introduce_a_pack.md)
344344
- [C++26 `constexpr`構造化束縛の許可と、`constexpr`参照の制限緩和](/lang/cpp26/constexpr_structured_bindings_and_references_to_constexpr_variables.md)
345+
- [C++26 コンパイル時のタプルやリストを展開処理する`template for`文](/lang/cpp26/expansion_statements.md)
345346
346347
347348
## 参照

lang/cpp26.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ C++26とは、2026年中に改訂される予定の、C++バージョンの通
7575
| 言語機能 | 説明 |
7676
|----------|------|
7777
| [静的リフレクション](/lang/cpp26/reflection.md.nolink) | リフレクション演算子`^^``std::meta::info`型によりコンパイル時にさまざまな情報を取得できる |
78-
| [コンパイル時のタプルやリストを展開処理する`template for`](/lang/cpp26/expansion_statements.md.nolink) | クラス・タプル・Range・パラメータパックなどを展開してすべての要素をコンパイル時の処理する`template for`文を追加 |
78+
| [コンパイル時のタプルやリストを展開処理する`template for`](/lang/cpp26/expansion_statements.md) | タプル・構造体・Range・パラメータパックなどの各要素に対してコンパイル時に文を展開する`template for`文を追加 |
7979
| [定数式での`void*`からポインタ型へのキャストを許可](/lang/cpp26/constexpr_cast_from_voidptr.md) | 型消去のために`void*`からポインタ型へのキャストを許可する |
8080
| [`static_assert`の診断メッセージにユーザーが生成した文字列の指定を許可](/lang/cpp26/user-generated_static_assert_messages.md) | `constexpr``S.size()``S.data()`メンバ関数をもつオブジェクトをコンパイル時文字列として指定できるようにする |
8181
| [`constexpr`配置`new`](/lang/cpp26/constexpr_placement_new.md) | 定数式の文脈での配置`new`を許可 |
@@ -523,7 +523,7 @@ C++26とは、2026年中に改訂される予定の、C++バージョンの通
523523
- [`<type_traits>`](/reference/type_traits.md)に、共用体の指定されたメンバがアクティブかを定数式で判定するための関数として[`std::is_within_lifetime()`](/reference/type_traits/is_within_lifetime.md)を追加
524524
- [`<type_traits>`](/reference/type_traits.md)に、仮想継承の関係を判定する[`std::is_virtual_base_of`](/reference/type_traits/is_virtual_base_of.md)を追加
525525
- [`<type_traits>`](/reference/type_traits.md)に、[`std::integral_constant`](/reference/type_traits/integral_constant.md)クラスを置き換える定数ラッパーとして[`std::constant_wrapper`](/reference/type_traits/constant_wrapper.md.nolink)クラスを追加
526-
- [`std::index_sequence`](/reference/utility/index_sequence.md)クラスを[構造化束縛](/lang/cpp17/structured_bindings.md)[template for文](/lang/cpp26/expansion_statements.md.nolink)で使用できるようにするため、タプルインタフェースの特殊化を追加
526+
- [`std::index_sequence`](/reference/utility/index_sequence.md)クラスを[構造化束縛](/lang/cpp17/structured_bindings.md)[template for文](/lang/cpp26/expansion_statements.md)で使用できるようにするため、タプルインタフェースの特殊化を追加
527527

528528

529529
### 制約

lang/cpp26/expansion_statements.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# コンパイル時のタプルやリストを展開処理する`template for`[P1306R5]
2+
* cpp26[meta cpp]
3+
4+
<!-- start lang caution -->
5+
6+
このページはC++26に採用される見込みの言語機能の変更を解説しています。
7+
8+
のちのC++規格でさらに変更される場合があるため[関連項目](#relative-page)を参照してください。
9+
10+
<!-- last lang caution -->
11+
12+
## 概要
13+
C++26では、タプル、構造体、Range、パラメータパック展開リストなどの各要素に対して、コンパイル時に文を繰り返し展開する「展開文 (expansion statement)」として`template for`文を導入する。
14+
15+
```cpp
16+
auto tup = std::make_tuple(0, 'a', 3.14);
17+
template for (auto elem : tup) {
18+
std::println("{}", elem);
19+
}
20+
```
21+
* std::make_tuple[link /reference/tuple/make_tuple.md]
22+
23+
出力:
24+
25+
```
26+
0
27+
a
28+
3.14
29+
```
30+
31+
この`template for`文は、本体の文をコンパイル時に要素数分だけ展開する。上記のコードは以下と等価である:
32+
33+
```cpp
34+
{
35+
{ auto elem = get<0>(tup); std::println("{}", elem); }
36+
{ auto elem = get<1>(tup); std::println("{}", elem); }
37+
{ auto elem = get<2>(tup); std::println("{}", elem); }
38+
}
39+
```
40+
41+
各展開において要素の型が異なりうるため、通常の`for`文では実現できない処理が可能となる。
42+
43+
44+
## 構文
45+
46+
```
47+
template for ( init-statement(opt) for-range-declaration : expansion-initializer ) compound-statement
48+
```
49+
50+
`expansion-initializer`には以下の3種類の形式がある:
51+
52+
- 列挙展開: `{ expression-list }` — 引数パック展開や、任意のタプル指定
53+
- 反復展開 (Range) : `expression``begin()`/`end()`が定義され、要素数がコンパイル時に確定するRange
54+
- 分解展開 (構造体・タプル) : `expression` — 構造化束縛可能なオブジェクト
55+
56+
57+
## 展開の種類
58+
### 列挙展開
59+
ブレース区切りの式リストやパック展開を展開する。
60+
61+
```cpp
62+
template <typename... Ts>
63+
void print_all(Ts... elems) {
64+
template for (auto elem : {elems...}) {
65+
std::println("{}", elem);
66+
}
67+
}
68+
69+
print_all(1, 2.0, "three");
70+
```
71+
72+
パック展開のほか、任意の式のリストも使用できる:
73+
74+
```cpp
75+
template for (auto x : {1, 2.0, "three"}) {
76+
std::println("{}", x);
77+
}
78+
```
79+
80+
81+
### 反復展開
82+
`begin()`/`end()`が定義され、`end - begin`がコンパイル時に確定するRangeを展開する。
83+
84+
```cpp
85+
void f() {
86+
template for (constexpr int I : std::array{1, 2, 3}) {
87+
static_assert(I < 4);
88+
}
89+
}
90+
```
91+
92+
### 分解展開
93+
構造化束縛可能なオブジェクト(タプル、構造体など)を展開する。
94+
95+
```cpp
96+
struct Point { int x; int y; int z; };
97+
98+
consteval long total_size(Point p) {
99+
long result = 0;
100+
template for (auto field : p) {
101+
result += sizeof(field);
102+
}
103+
return result;
104+
}
105+
```
106+
107+
範囲としても分解としても解釈できる場合は、範囲が優先される。
108+
109+
110+
## `break`と`continue`
111+
`template for`文の本体内で`break`と`continue`を使用できる。これらは実行時の制御フローとして動作する。
112+
113+
- `break` : 展開全体の末尾に飛ぶ
114+
- `continue` : 次の展開の先頭に飛ぶ
115+
116+
同じ型の要素のみのタプルであれば、通常の`if`文で条件分岐できる:
117+
118+
```cpp
119+
auto tup = std::make_tuple(1, 2, 3);
120+
template for (auto elem : tup) {
121+
if (elem == 2) break;
122+
std::println("{}", elem);
123+
}
124+
// 出力: 1
125+
```
126+
127+
異なる型の要素をもつタプルでは、型に依存する条件を`if constexpr`で分岐する必要がある:
128+
129+
```cpp
130+
auto tup = std::make_tuple(1, "hello", 3.14);
131+
template for (auto elem : tup) {
132+
// 整数型の場合のみ値を判定してcontinueする
133+
if constexpr (std::is_integral_v<decltype(elem)>) {
134+
if (elem == 1) continue;
135+
}
136+
std::println("{}", elem);
137+
}
138+
// 出力:
139+
// hello
140+
// 3.14
141+
```
142+
* std::is_integral_v[link /reference/type_traits/is_integral.md]
143+
144+
145+
## `return`
146+
`template for`文の本体内で`return`を使用すると、所属する関数から直接返る。これは[`std::apply()`](/reference/tuple/apply.md)にラムダ式を渡す方法では実現できなかった利点である。
147+
148+
```cpp
149+
// タプルの要素から最初の整数型の値を探して返す
150+
template <class Tuple>
151+
std::optional<int> find_first_int(Tuple&& tup) {
152+
template for (auto&& elem : tup) {
153+
if constexpr (std::is_integral_v<std::remove_cvref_t<decltype(elem)>>) {
154+
return elem; // 所属する関数から直接返る
155+
}
156+
}
157+
return std::nullopt;
158+
}
159+
160+
auto tup = std::make_tuple("hello", 42, 3.14);
161+
auto result = find_first_int(tup); // 42
162+
```
163+
* std::is_integral_v[link /reference/type_traits/is_integral.md]
164+
* std::remove_cvref_t[link /reference/type_traits/remove_cvref.md]
165+
* std::make_tuple[link /reference/tuple/make_tuple.md]
166+
167+
168+
## `co_await`
169+
`template for`文の本体内で`co_await`を使用すると、所属するコルーチンを直接サスペンドできる。ラムダ式内の`co_await`は囲む関数ではなくラムダ自体をコルーチンにしてしまうため、従来の方法では実現が困難であった。
170+
171+
```cpp
172+
// タプルの各要素に対して非同期処理を順番に実行する
173+
template <class Tuple>
174+
Task process_all(Tuple&& tup) {
175+
template for (auto&& elem : tup) {
176+
co_await async_process(elem); // 所属するコルーチンをサスペンドする
177+
}
178+
}
179+
```
180+
181+
182+
## この機能が必要になった背景・経緯
183+
C++23以前では、[`std::tuple`](/reference/tuple/tuple.md)のような異種型コンテナの各要素を処理するために、テンプレートの再帰的インスタンス化や[`std::apply()`](/reference/tuple/apply.md)を使用する必要があった。これらの手法には以下の問題があった:
184+
185+
- コンパイル時間とメモリの消費が大きい
186+
- ラムダ式内での`return``co_await`が囲む関数に影響を与えない
187+
- コードが不自然で読みにくい
188+
189+
`template for`文はこれらの問題を解決し、異種型コンテナの反復処理を通常の`for`文と同様の構文で記述できるようにする。
190+
191+
192+
## <a id="relative-page" href="#relative-page">関連項目</a>
193+
- [C++11 可変引数テンプレート](/lang/cpp11/variadic_templates.md)
194+
- [C++17 畳み込み式](/lang/cpp17/folding_expressions.md)
195+
- [C++17 構造化束縛](/lang/cpp17/structured_bindings.md)
196+
- [C++26 構造化束縛でパックを導入できるようにする](/lang/cpp26/structured_bindings_can_introduce_a_pack.md)
197+
- [C++26 静的リフレクション](/lang/cpp26/reflection.md.nolink)
198+
- [`std::tuple`](/reference/tuple/tuple.md)
199+
- [`std::apply()`](/reference/tuple/apply.md)
200+
201+
202+
## 参照
203+
- [P1306R5 Expansion Statements](https://open-std.org/jtc1/sc22/wg21/docs/papers/2025/p1306r5.html)

lang/cpp26/feature_test_macros.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
|`__cpp_constexpr_virtual_inheritance`|`202506L`|`constexpr`仮想継承を許可|
2121
|`__cpp_contracts`|`202502L`|[契約プログラミングをサポートする](/lang/cpp26/contracts.md)|
2222
|`__cpp_deleted_function`|`202403L`|[関数宣言を削除する理由を指定できるようにする](/lang/cpp26/delete_reason.md)|
23-
|`__cpp_expansion_statements`|`202506L`|[コンパイル時のタプルやリストを展開処理する`template for`](/lang/cpp26/expansion_statements.md.nolink)|
23+
|`__cpp_expansion_statements`|`202506L`|[コンパイル時のタプルやリストを展開処理する`template for`](/lang/cpp26/expansion_statements.md)|
2424
|`__cpp_impl_reflection`|`202506L`|静的リフレクション|
2525
|`__cpp_pack_indexing`|`202311L`|[パラメータパックへのインデックスアクセスを許可](/lang/cpp26/pack_indexing.md)|
2626
|`__cpp_placeholder_variables`|`202306L`|[宣言のみで使用しない変数の名前として`_`をサポート](/lang/cpp26/nice_placeholder_with_no_name.md)|

reference/tuple/apply.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ hello
129129
- [`apply_result`](/reference/type_traits/apply_result.md)
130130
- [`is_applicable`](/reference/type_traits/is_applicable.md)
131131
- [`is_nothrow_applicable`](/reference/type_traits/is_nothrow_applicable.md)
132+
- [C++26 コンパイル時のタプルやリストを展開処理する`template for`文](/lang/cpp26/expansion_statements.md)
132133
133134
134135
## 参照

reference/tuple/tuple.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ after b: i=0, d=0
261261
## 関連項目
262262
- [`std::pair`](/reference/utility/pair.md)
263263
- [C++17 構造化束縛](/lang/cpp17/structured_bindings.md)
264+
- [C++26 コンパイル時のタプルやリストを展開処理する`template for`文](/lang/cpp26/expansion_statements.md)
264265
265266
## 参照
266267
- [タプル - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%BF%E3%83%97%E3%83%AB)

0 commit comments

Comments
 (0)