次期C++2b(C++23)標準ライブラリに追加されるstd::generator<R>
クラステンプレートについてメモ。
提案文書P2502R2よりコード引用:*1
// C++2b #include <functional> #include <generator> #include <range> #include <utility> std::generator<int> fib() { auto a = 0, b = 1; while (true) { co_yield std::exchange(a, std::exchange(b, a + b)); } } int answer_to_the_universe() { auto rng = fib() | std::views::drop(6) | std::views::take(3); return std::ranges::fold_left(std::move(rng), 0, std::plus{}); }
コルーチンfib
から生成されるフィボナッチ数列 0, 1, 1, 2, 3, 5, 8, 13, 21... のうち、先頭6要素を破棄(drop)した後に3要素のみ取出す(take)遅延評価レンジrng
を作成する(この時点ではコルーチン本体処理は開始されない)。rng
に対して初期値 0 と算術加算(plus)による畳み込み(fold)操作を行い、結果値 42 を得る。
まとめ:
- ユーザ定義コルーチンの記述で利用する
R
型の値を生成するジェネレータ(generator)型。Rangesライブラリとの高い親和性をもつ。co_yield val
による単一値の生成。co_yield elements_of(range)
による複数値の逐次生成。- コルーチン本体処理は遅延評価される。*2
- 新しい標準ヘッダ<generator>*3
- クラステンプレート
std::generator<R, V, Allocator>
(V
,Allocator
は省略可) generator
はview
コンセプトとinput_range
コンセプトのモデル。範囲for文や各種レンジアダプタ(range adaptor)と組み合わせて利用可能。
- クラステンプレート
- 標準ヘッダ<ranges>の拡張
std::ranges::elements_of<R, Allocator>
クラステンプレート(R
はrange
コンセプトのモデル、Allocator
は省略可)elements_of
はco_yield式に対するタグ型として機能する。
- ジェネレータ利用側イテレータの提供型
generator<R>
型(V
省略時)generator<R, V>
型co_yield val
- co_yield式の評価結果は
void
。 - co_yield式に指定する右辺値
val
はコピーされない(R
=値型T
のシンプルなケースを想定)。 - 内部的には指定値へのアドレス情報のみを保持しており(
value_ = std::addressof(val)
)、ジェネレータ利用側イテレータで実体(*value_
)をReference型へとキャストする。 - co_yield式に指定する左辺値
val
はAwaitableオブジェクト内部にコピー格納され、そのアドレス情報が保持される。 - 2022-08-10追記:テンプレートパラメータ
R
へ指定する型とコピー/ムーブの関係性は id:yohhoy:20220810 を参照。
- co_yield式の評価結果は
co_yield elements_of(range)
- co_await式はサポートしない。
- コルーチンから送出された例外はジェネレータ利用側にそのまま伝搬する(→id:yohhoy:20211112)。
co_yield elements_of(child)
の子コルーチンchild
の送出例外は、親コルーチン側でもcatch可能。ハンドリングしなければ親コルーチンが返すジェネレータ利用側へと伝搬する。
- アロケータサポート
- コルーチンフレーム*7のメモリ確保/解放に用いられる。
- テンプレートパラメータ
Allocator
型またはコルーチン引数にてstd::allocator_arg_t
+アロケータ型を指定する。 - アロケータの型消去(type erasure)をサポート。例:
std::generator<int>
型は任意のアロケータ型をコルーチンフレーム上に格納する。 - ステートレス/ステートフル両アロケータをサポート。
- 動作性能はC++コンパイラの最適化性能に強く依存する(→id:yohhoy:20180415)。
関連URL
- (PDF) P2502R2 std::generator Synchronous Coroutine Generator for Ranges
- P2529R0 generator should have T&& reference_type
- https://github.com/cplusplus/papers/issues/1151
- [C++]WG21月次提案文書を眺める(2022年06月) - 地面を見下ろす少年の足蹴にされる私
- cppreference: Coroutines, cpprefjp: コルーチン
- 20分くらいでわかった気分になれるC++20コルーチン - yohhoyの日記
- コルーチン×ラムダ式キャプチャ=鼻から悪魔 - yohhoyの日記
*1:std::ranges::fold_left アルゴリズムは P2322R6 にてC++2b標準ライブラリへ追加予定となっている。
*2:std::generator<R>::promise_type 型の初期サスペンドポイント(initial_suspend)は suspend_always 型を返す。
*3:C++20 <coroutine>ヘッダはコルーチン機構実装用の低レイヤ機能のみを提供する。
*4:Value型は std::generator それ自身からは参照されず、利用側で イテレータの value_type 型にアクセスするユースケース向け。
*5:テンプレートパラメータ R に対して "reference collapsing" 規則が適用される。例: R=const T& ならば R&&=const T& となる。
*6:std::generator<R>::promise_type 型の yield_value メンバ関数が返すAwaiter型では、await_suspend メンバ関数が転送先コルーチンの std::coroutine_handler 値を返す。