C++教科書/標準ライブラリ編/<generator> の章

編集

はじめに

編集

C++23では、非同期コードを同期的な構文で記述できる新しい言語機能であるコルーチンが導入されました。ジェネレータは、コルーチンの中でも、一連の値を生成できる特別な種類のコルーチンです。

この章では、C++23における<generator>ヘッダーで提供されるジェネレータの機能について解説します。

概念

編集

まず、ジェネレータを理解する前に、viewという概念について説明します。viewは、範囲ライブラリで使用される軽量なデータソース表現です。範囲ライブラリは、汎用的なシーケンスを操作するためのライブラリですが、この章では詳細を省略します。

ジェネレータは、viewを継承したクラステンプレートであり、同期コルーチンジェネレータを表します。つまり、ジェネレータは、非同期処理を同期的な構文で記述できるコルーチンの一種です。

std::generatorクラステンプレート

編集

std::generatorは、ジェネレータを表すクラステンプレートです。以下のテンプレートパラメータを持ちます。

  • Ref: ジェネレータが生成するデータの参照型
  • V (省略可能): ジェネレータが生成する値の型 (デフォルトはvoid)
  • Allocator (省略可能): ジェネレータが使用するメモリの管理に用いるアロケータ (デフォルトはvoid)

std::pmr::generatorは、多態性メモリアロケータを使用するための便利なエイリアスです。

std::generatorには、以下のメンバ関数が用意されています。

  • コンストラクタ:
    • コピーコンストラクタ (generator(const generator&)) は禁止されていますが、移動コンストラクタ (generator(generator&& other)) は効率的なリソース管理のために許可されています。
  • デストラクタ: (~generator()) は、適切なクリーンアップを行います。
  • 移動代入演算子: (generator& operator=(generator other)) は、効率的なリソース転送を行います。
  • イテレータ:
    • begin(): 生成されたシーケンスの先頭のイテレータを返します。
    • end(): シーケンスの終端を示す番兵値を返します (常にdefault_sentinel_tです)。

std::generator::promise_typeネストクラス

編集

std::generator::promise_typeは、std::generatorのネストクラスであり、コルーチンの実行と呼び出し側との通信を管理します。以下のメンバ関数が用意されています。

  • get_return_object(): コルーチンを表すジェネレータオブジェクトを返します。
  • initial_suspend(): 最初はサスペンドしないことを示します。
  • final_suspend(): 終了時のサスペンド処理を処理します (詳細は省略可)。
  • yield_value(): さまざまな型の値 (参照、rvalue参照、または別のジェネレータ) を生成するための関数。
  • return_void(): ジェネレータが値を生成しない場合に内部で使用されます。
  • unhandled_exception(): ジェネレータコルーチン内でスローされた例外を処理します。
  • メモリの割り当てと解放のための関数 (operator newoperator delete) は、内部メモリ管理に使用されます。

std::generator::iteratorネストクラス

編集

std::generator::iteratorは、ジェネレータが生成する値のシーケンスを反復するための軽量なイテレータ型です。以下のメンバ関数が用意されています。

  • operator*: 現在のイテレータが指す値を返します。
  • operator++: イテレータをインクリメントします。
  • operator==: イテレータがシーケンスの終端に達しているかどうかを比較します。

以下は、ジェネレータを使用して1から10までの整数を生成する例です。

#include <generator>
#include <iostream>

std::generator<int> generate_integers(int start, int end) {
  for (int i = start; i <= end; i++) {
    co_yield i;
  }
}

int main() {
  for (int i : generate_integers(1, 10)) {
    std::cout << i << " ";
  }
  std::cout << std::endl;
}

ジェネレータの利用例

編集

ジェネレータは、さまざまな用途に使用できます。以下に、いくつかの例を示します。

  • 遅延評価: ジェネレータを使用して、必要なときにのみ値を計算する遅延評価アルゴリズムを実装できます。これは、メモリ使用量とパフォーマンスを向上させるのに役立ちます。
  • 非同期処理: ジェネレータを使用して、非同期処理を同期的な構文で記述できます。これは、ネットワーク I/O やファイル入出力などのタスクに役立ちます。
  • データストリーム: ジェネレータを使用して、外部データソースからデータストリームを生成できます。これは、ファイルやネットワーク接続からのデータの処理に役立ちます。

まとめ

編集

C++23における<generator>ヘッダーは、コルーチンとジェネレータを使用して、非同期コードを同期的な構文で記述するための強力なツールを提供します。ジェネレータは、さまざまな用途に使用でき、コードの可読性と保守性を向上させるのに役立ちます。

補足

編集
  • この章では、ジェネレータの基本的な概念と機能について説明しました。より詳細な情報は、C++23 標準規格またはその他の資料を参照してください。
  • ジェネレータは、C++23 の新機能であり、すべてのコンパイラでサポートされているわけではありません。使用前に、コンパイラのドキュメントを確認してください。