最終更新日時: 2026年04月02日 14時29分58秒 (JST)
teruyamato0731 が更新

履歴 編集

class template
<generator>

# std::generator(C++23)

    namespace std {
      template<class Ref, class V = void, class Allocator = void>
      class generator : public ranges::view_interface<generator<Ref, V, Allocator>> {
        ...
      };

      namespace pmr {
        template<class R, class V = void>
        using generator = std::generator<R, V, polymorphic_allocator<>>;
      }
    }

## 概要

`generator`クラステンプレートは、コルーチンの評価により生成される要素列のビュー(view)を表現する。 特殊化された`generator`は`view`および`input_range`のモデルである。 `generator`はムーブのみ可能なオブジェクトである。

戻り値型`generator`のコルーチン（以下、ジェネレータコルーチン）では`co_yield`式を用いて値を生成する。`co_yield std::ranges::elements_of(rng)`式を用いると、ジェネレータコルーチンから入れ子Range(`rng`)の各要素を逐次生成する。 ジェネレータコルーチンでは`co_await`式を利用できない。

ジェネレータコルーチンは遅延評価される。 ジェネレータコルーチンが返す`generator`オブジェクトを利用するコード（以下、ジェネレータ利用側）において、先頭要素を指すイテレータを取得する(`begin`)、またはイテレータのインクリメント操作を行うまでジェネレータコルーチンは再開(resume)されない。 ジェネレータコルーチン本体処理において`co_yield`式に到達すると、生成値を保持してから中断(suspend)しジェネレータ利用側へと制御を戻す。 ジェネレータ利用側ではイテレータの間接参照を行うことで、ジェネレータによる生成値を取得する。

### 説明用メンバ

`generator`では下記の説明用メンバ型を定義する。

- `value` : `conditional_t<is_void_v<V>, remove_cvref_t<Ref>, V>`
- `reference` : `conditional_t<is_void_v<V>, Ref&&, Ref>`
- `iterator` : ジェネレータが返すイテレータ型。

`generator`および`promise_type`と`iterator`の動作説明のため、下記の説明用メンバを用いる。

- `coroutine_` : `coroutine_handle<promise_type>`型のコルーチンハンドル
- `active_` : `unique_ptr<stack<coroutine_handle<>>>`型のアクティブスタック

### 第1テンプレートパラメータ`Ref`の概要

`generator`クラステンプレートでは、テンプレートパラメータ`Ref`, `V`の組み合わせによってco_yieldオペランド型(`yielded`)やイテレータ間接参照の結果型(`reference`)やイテレータの値型(`value_type`)を制御する。

第1パラメータ`Ref`以外は省略可能となっており、ほとんどのユースケース（提案文書によれば98%）では、テンプレートパラメータ`Ref`のみの明示指定で十分とされる。 値型`T`を生成するジェネレータにおいて、第1テンプレートパラメータ`Ref`に応じて関連する型がそれぞれ導出される。

`Ref` | co_yieldオペランド型 | 間接参照の結果型 | イテレータの値型
--- | --- | --- | ---
`T` `T&&` | `T&&` と `const T&` | `T&&` | `T`
`T&` | `T&` | `T&` | `T`
`const T&` | `const T&` | `const T&` | `T`

ジェネレータそれ自身ではイテレータの値型を直接利用しない。ジェネレータ利用側でイテレータの`value_type`型にアクセスする特殊な追加処理を行う場合のみ、第2テンプレートパラメータ`V`の明示指定が必要とされる。

### ジェネレータと値のコピー／ムーブ

`generator`クラステンプレートでは、その内部動作において最低限のコピー／ムーブ処理しか行わないよう設計されている。このため一定条件を満たせば、コピー不可でムーブのみ可能な型や、コピー／ムーブいずれも不可能な型の生成もサポートする。

値型`T`を生成するジェネレータにおいて、第1テンプレートパラメータ`Ref`とco_yield演算子オペランドに指定する式（左辺値／右辺値やconst修飾）に応じて、ジェネレータコルーチン最大1回のコピーが発生する。 `Ref=T&`とした場合、co_yieldに右辺値を指定するとコンパイルエラーになる。

`Ref`＼co_yield式 | 左辺値 | const左辺値 | 右辺値
--- | --- | --- | ---
`T` `T&&` | コピー 1回 | コピー 1回 | 0回
`T&` | コピー 1回 | コピー 1回 | (不適格)
`const T&` | 0回 | 0回 | 0回

またジェネレータ利用側においては、イテレータ間接参照結果を受ける変数の宣言型`X`との関係に応じて（例：範囲for文の変数宣言型）、最大で1回のコピーまたはムーブが生じる。 `Ref=T&`または`Ref=const T&`とした場合、右辺値参照型`T&&`には束縛できないためコンパイルエラーとなる。

`Ref`＼`X` | `T` | `T&&` | `const T&`
--- | --- | --- | ---
`T` `T&&` | ムーブ 1回 | 0回 | 0回
`T&` | コピー 1回 | (不適格) | 0回
`const T&` | コピー 1回 | (不適格) | 0回

ムーブのみ可能な型（例：`std::unique_ptr`）では、上記表においてコピーが生じる組み合わせを避ければジェネレータより生成可能である。 同様にコピー／ムーブいずれも不可能な型では、コピーまたはムーブが生じる組み合わせを避ければ生成可能である。

### アロケータサポート

`generator`クラステンプレートの第3テンプレートパラメータ`Allocator`によって、コルーチン・ステートの動的メモリ確保に用いる静的アロケータ型を指定できる。省略時はデフォルトアロケータ`allocator<void>`が利用される。

またジェネレータコルーチン定義の引数リストに`allocator_arg`タグ型に続いてアロケータオブジェクトを指定すると、ジェネレータの生成毎に異なるアロケータ利用を指定することもできる。

C++コンパイラによっては、ジェネレータコルーチンに関する動的メモリ確保・解放処理は最適化によって省略され(coroutine elision)、より効率的なコードが生成されることも期待できる。

### ジェネレータのネスト

別の子ジェネレータコルーチンが返す`generator`もRangeとなっているため、ある親ジェネレータコルーチン内部で`co_yield std::ranges::elements_of`構文を用いると、複数ジェネレータコルーチンのネスト構造をとることができる。 子ジェネーレタコルーチンの遅延評価によって生成される値は、親ジェネレータコルーチンから生成されたかのように振る舞う。

`generator`クラス（厳密には`generator::promise_type`）ではジェネレータコルーチンのネスト構造を検出し、対称コルーチンとしてコルーチン間の実行フロー転送制御を効率的に行う。

## 適格要件

- テンプレートパラメータ`Allocator`が`void`ではない場合、`allocator_traits<Allocator>::pointer`はポインタ型であること。
- 説明用のメンバ型`value`はCV修飾されないオブジェクト型であること。
- 説明用のメンバ型`reference`は参照型、または`copy_constructible`のモデルであるCV修飾されないオブジェクト型であること。
- 説明用のメンバ型`reference`が参照型の場合には`RRef` == `remove_reference_t<reference>&&`、それ以外の場合には`RRef` == `reference`としたとき、それぞれ下記のモデルであること。
  - `common_reference_with<reference&&, value&>`
  - `common_reference_with<reference&&, RRef&&>`
  - `common_reference_with<RRef&&, const value&>`

テンプレートパラメータ`Allocator`が`void`ではない場合、`Cpp17Allocator`の要件を満たすこと。

## メンバ関数

### 構築・破棄

名前 | 説明 | 対応バージョン
--- | --- | ---
`(constructor)` | コンストラクタ | C++23
`(destructor)` | デストラクタ | C++23
`operator=(generator other) noexcept;` | ムーブ代入演算子 | C++23

### イテレータ

名前 | 説明 | 対応バージョン
--- | --- | ---
`begin` | Viewの先頭を指すイテレータを取得する | C++23
`end` | Viewの番兵を取得する | C++23

## メンバ型

名前 | 説明 | 対応バージョン
--- | --- | ---
`yielded` | `co_yield`演算子オペランド型（後述） | C++23
`promise_type` | ジェネレータコルーチンのPromise型 | C++23

    using yielded =
      conditional_t<is_reference_v<reference>, reference, const reference&>;

## 例

### 例1: 単一値の生成

    #include <generator>
    #include <ranges>
    #include <iostream>

    // 偶数値列を無限生成するコルーチン
    std::generator<int> evens()
    {
      int n = 0;
      while (true) {
        co_yield n;
        n += 2;
      }
    }

    int main()
    {
      // ジェネレータにより生成されるRangeのうち
      // 先頭から5個までの要素値を列挙する
      for (int n : evens() | std::views::take(5)) {
        std::cout << n << std::endl;
      }
    }

#### 出力

    0
    2
    4
    6
    8

### 例2: Range要素値の逐次生成

    #include <generator>
    #include <iostream>
    #include <list>
    #include <ranges>
    #include <vector>

    // Rangeの要素値を逐次生成するコルーチン
    std::generator<int> ints()
    {
      int arr[] = {1, 2, 3};
      co_yield std::ranges::elements_of(arr);
      std::vector<int> vec = {4, 5, 6};
      co_yield std::ranges::elements_of(vec);
      std::list<int> lst = {7, 8, 9};
      co_yield std::ranges::elements_of(lst);
    }

    int main()
    {
      for (int n : ints()) {
        std::cout << n << ' ';
      }
    }

#### 出力

    1 2 3 4 5 6 7 8 9

### 例3: ジェネレータのネスト

    #include <generator>
    #include <iostream>
    #include <ranges>
    #include <memory>

    // 二分木ノード
    struct node {
      int value;
      std::unique_ptr<node> left = nullptr;
      std::unique_ptr<node> right = nullptr;
    };

    // 二分木を走査: 左(left)→自ノード→右(right)
    std::generator<int> traverse(const node& e)
    {
      if (e.left) {
        co_yield std::ranges::elements_of(traverse(*e.left));
      }
      co_yield e.value;
      if (e.right) {
        co_yield std::ranges::elements_of(traverse(*e.right));
      }
    }

    int main()
    {
      // tree:
      //    2
      //   / ¥
      //  1   4
      //     / ¥
      //    3   5
      node tree = {
        2,
        std::make_unique<node>(1),
        std::make_unique<node>(
          4,
          std::make_unique<node>(3),
          std::make_unique<node>(5)
        ),
      };

      for (int n: traverse(tree)) {
        std::cout << n << std::endl;
      }
    }

#### 出力

    1
    2
    3
    4
    5

### 例4: 第2テンプレートパラメータVの利用

    #include <iostream>
    #include <generator>
    #include <ranges>
    #include <string>
    #include <string_view>
    #include <vector>

    // Ref=string_view, V=string のジェネレータコルーチン
    auto fizzbuzz() ->
      std::generator<std::string_view, std::string>
    {
      for (size_t i = 1; ; ++i) {
        if (i % 15 == 0) {
          co_yield "FizzBuzz";
        } else if (i % 3 == 0) {
          co_yield "Fizz";
        } else if (i % 5 == 0) {
          co_yield "Buzz";
        } else {
          co_yield std::to_string(i);
        }
      }
    }

    int main()
    {
      // std::ranges::to<C>() は変換元Rangeの range_value_t を利用して戻り値型を決定する。
      // ここでは std::vector<std::string> 型を導出するために、ジェネレータコルーチンの
      // 第2テンプレートパラメータ V = std::string として明示指定する必要がある。
      auto vec = fizzbuzz() | std::views::take(15) | std::ranges::to<std::vector>();

      // もし fizzbuzz() 戻り値型が std::generator<std::string_view> であった場合、
      // 変数 vec は std::vector<std::string_view> 型となる。このときコルーチン内部実装の
      // std::to_string() の戻り値 std::string オブジェクトはco_yield式末尾で寿命が切れるため、
      // 各要素 vec[i] に格納される std::string_view はダングリング状態になってしまう。

      for (const auto& e : vec) {
        std::cout << e << std::endl;
      }
    }

#### 出力

    1
    2
    Fizz
    4
    Buzz
    Fizz
    7
    8
    Fizz
    Buzz
    11
    Fizz
    13
    14
    FizzBuzz

## バージョン

### 言語

- C++23

### 処理系

- Clang:
- GCC:
- ICC:
- Visual C++:

## 関連項目

- `std::ranges::elements_of`

## 参照

- P2502R2 `std::generator`: Synchronous Coroutine Generator for Ranges
- P2787R1 pmr::generator - Promise Types are not Values
- P0981R0 Halo: coroutine Heap Allocation eLision Optimization: the joint response
  - ジェネレータコルーチン実装において、動的メモリ確保が省略最適化される条件の議論。`std::generator`の設計に反映されている。