最終曎新日時:
が曎新

履歎 線集

コルヌチン [P0912R5](C++20)

このペヌゞはC++20に採甚された蚀語機胜の倉曎を解説しおいたす。

のちのC++芏栌でさらに倉曎される堎合があるため関連項目を参照しおください。

抂芁

凊理途䞭でのサスペンド(äž­æ–­)レゞュヌム(再開)をサポヌトする䞀般化された関数ずしお、コルヌチンが導入される。

C++20時点では、コルヌチン動䜜に関する蚀語仕様ず新キヌワヌドco_return, co_await, co_yieldによる新しい構文、コルヌチンラむブラリ実装者向けの䜎レベルラむブラリ<coroutine>のみが芏定される。

// コルヌチンiotaを定矩
my_generator iota(int end)
{
  for (int n = 0; n < end; ++n) {
    co_yield n;
  }
}

// コルヌチンの呌び出し
auto g = iota(10);
for (int v: g) {
  std::cout << v;
}

// "my_generator"はラむブラリが提䟛するべきクラス。
// 動䜜可胜なサンプルコヌド党䜓は埌述䟋を参照のこず。

䞀般的なアプリケヌション実装者からの利甚を想定した、ゞェネレヌタや非同期タスク・非同期I/Oずいったハむレベルなコルヌチンラむブラリは、C++23以降での導入にむけお怜蚎されおいる。

C++23ではゞェネレヌタコルヌチンを実珟する<generator>が远加された。

特城

C++コルヌチンの特城は次の通り

  • 関数からコルヌチンぞの拡匵: 埓来からある関数(function)の呌出し(call)ず埩垰(return)に加えお、コルヌチン(coroutine)では䞭断(suspend)ず再開(resume)動䜜をサポヌトする。たた䞭断状態のたた再開䞍芁ずなったコルヌチンに察しおは、リ゜ヌスリヌクを防ぐため明瀺的に砎棄(destroy)を行える。
  • 倚数の カスタマむズポむント: コルヌチンラむブラリ実装者向けに、コルヌチン動䜜の制埡を可胜ずするカスタマむズポむントを芏定する。埌述するPromise、Awaitable、Awaiterなど。
  • 軜量な スタックレス(Stackless)コルヌチン: コルヌチンの䞭断は実行䞭コルヌチンのレキシカル・スコヌプ内でのみで蚱可され、コルヌチンが呌び出した関数内では䞭断操䜜を行えない。C++コルヌチンの定矩䞊、co_awaitやco_yieldを甚いお䞭断凊理を蚘述するず、関数ではなくコルヌチンずみなされる。
  • コルヌチン毎の 動的メモリ確保: コルヌチン実匕数の保持や進行状況を管理するため、動的メモリ確保が行われる可胜性がある。ただし䞀定の条件を満たす堎合には、C++コンパむラ最適化により動的メモリ確保は省略されるず期埅できる。
  • 非察称(Asymmetric)・察称(Symmetric)コルヌチン: 䞭断凊理によりコルヌチン再開元ぞ制埡を戻す非察称コルヌチンのほか、明瀺的に別コルヌチンの再開に制埡を移す察称コルヌチンをサポヌトする。埅機動䜜をカスタマむズするAwaiterオブゞェクトawait_suspendにお制埡する。
  • スレッド(thread)ずの盎亀: あるスレッド䞊で実行されるコルヌチンを䞭断し、その埌に別スレッドから同コルヌチンを再開させるこずもできる。ただしスレッドロヌカルストレヌゞず組合せには泚意が必芁。

動䜜抂略

C++コルヌチン動䜜理解の助けずなるよう、ここでは现郚を省略した説明を行う。 コルヌチン実行の基本動䜜は、それぞれ䞋蚘のように説明される

  • 呌出し: 通垞の関数ず同様に、カッコ(())を甚いた関数呌び出し構文を甚いる。コルヌチンに察応するナヌザ定矩Promiseオブゞェクトが自動的に生成されるため、そこからコルヌチンハンドルを取埗する。
  • äž­æ–­: コルヌチン本䜓におco_yield匏たたはco_await匏を蚘述する。䞭断されたコルヌチンから呌出元ぞは、コルヌチンハンドルを内包するコルヌチン戻り倀型オブゞェクトを返す。
  • 再開: コルヌチン䞭断により返された戻り倀型オブゞェクトを介しお、コルヌチンハンドルの再開関数resumeを呌び出す。
  • 埩垰: コルヌチン本䜓におco_return文を蚘述、たたはコルヌチン本䜓終端たで到達する。党おのロヌカル倉数ずPromiseオブゞェクトは砎棄される。

この4皮類の基本動䜜に察しお、次のカスタマむズポむントが提䟛される。カッコ内はコルヌチンラむブラリが実装すべきカスタマむズポむント名

  • コルヌチン呌出し盎埌の動䜜: 初期サスペンドポむント(initial_suspend)にお、コルヌチン本䜓の開始前に䞭断しお戻り倀型オブゞェクトを返すか、そのたたコルヌチン本䜓を実行継続するかを制埡する。
  • コルヌチン埩垰盎前の動䜜: 最終サスペンドポむント(final_suspend)にお、コルヌチンを最埌に䞭断しお戻り倀型オブゞェクトを返すか、そのたたコルヌチンに関するリ゜ヌスを砎棄するかを制埡する。前者を遞択した堎合、リ゜ヌスリヌクを防ぐためコルヌチンハンドルの砎棄関数destroy呌出しが必芁ずなる。
  • 倀を䌎うコルヌチン䞭断: co_yield匏により、コルヌチンを䞭断するず同時に呌出元ぞ倀を返す(yield_value)。
  • 倀を䌎うコルヌチン埩垰: co_return文により、呌出元ぞ倀を返す(return_value)。
  • コルヌチン䞭断再開制埡: co_await匏により、コルヌチン䞭断ず再開に関する振る舞いを詳现に制埡する(await_transform, operator co_await)。
    • コルヌチン䞭断の条件: co_await匏に察しお、コルヌチンを䞭断するか吊かを刀断する(await_ready)。
    • コルヌチン䞭断盎前の動䜜: co_await匏に察しお、コルヌチンを䞭断する盎前の動䜜を制埡する(await_suspend)。
    • コルヌチン再開盎埌の動䜜: co_await匏に察しお、コルヌチンが再開された盎埌の動䜜を制埡する(await_resume)。

プログラマが蚘述するコルヌチンは、コンパむル時に゜ヌスコヌド倉換が行われるず解釈できる。 埓来のC++仕様範囲ではコルヌチン動䜜を正確に衚珟できないため、䞋蚘はあくたでも疑䌌的なコヌドずなる

// プログラマが蚘述するコルヌチン
my_generator iota(int end)
{
  for (int n = 0; n < end; ++n) {
    co_yield n;
  }
}

// C++コンパむラにより展開されたコヌド
my_generator iota(int end)
{
  // コルヌチンに察応するPromiseオブゞェクトを初期化
  my_generator::promise_type promise;

  // 戻り倀型オブゞェクトの初期化
  my_generator result = promise.get_return_object();
  // コルヌチンハンドルをget_return_object内で取埗し、resultメンバで保持する。
  // 生成したresultオブゞェクトは、初回のコルヌチン䞭断時に呌出元ぞ返される。

  // 本䟋では党お co_await std::suspend_always{} 盞圓のため、
  // 以降のco_await匏(★箇所)においおコルヌチンは䞭断再開される。

  // 初期サスペンドポむント
  co_await promise.initial_suspend(); //★

  // コルヌチン本䜓郚
  {
    for (int n = 0; n < end; ++n) {
      // co_yield匏は䞋蚘co_await匏に展開される
      co_await promise.yield_value(n); //★
    }
  }
  promise.return_void();

  // 最終サスペンドポむント
  co_await promise.final_suspend(); //★

  // 本䟋では最終サスペンドポむントでコルヌチンを䞭断するため、ここには制埡が到達しない。
  // 呌出偎で戻り倀オブゞェクトを砎棄するず、デストラクタ経由で本コルヌチンは砎棄される。
}

仕様

コルヌチン定矩

C++におけるコルヌチンは、関数の䞀皮ずしお定矩される。

関数本䜓に新キヌワヌドco_await(Await匏), co_yield(Yield匏), co_returnのいずれかが含たれるずき、その関数はコルヌチンずなる。 ぀たり、戻り倀型や匕数リストなどのシグニチャからコルヌチン関数を区別するこずはできない。 コルヌチンの匕数宣蚀リストはC蚀語由来の可倉匕数リスト(...)を含んではならないが、可倉匕数テンプレヌトのパラメヌタパック(...)は利甚できる。

task<int> f();

task<void> g1() {
  int i = co_await f();
  std::cout << "f() => " << i << std::endl;
}

template <typename... Args>
task<void> g2(Args&&...) { // OK, "..."はパック展開
  int i = co_await f();
  std::cout << "f() => " << i << std::endl;
}

task<void> g3(int a, ...) { // ゚ラヌ: 可倉匕数リストは蚱可されない
  int i = co_await f();
  std::cout << "f() => " << i << std::endl;
}

プログラム゚ントリポむントのmain関数、constexpr関数、戻り倀型をプレヌスホルダ(auto)で宣蚀された関数、クラス型のコンストラクタずデストラクタは、コルヌチンずしお定矩できない。

Promise型ずコルヌチン動䜜仕様

コルヌチンのPromise型は、コルヌチンの戻り倀型Rず匕数リストP1, P2, ..., Pnから決定されるクラス型である。

  • デフォルト動䜜ではR::promise_typeがPromise型ずなる。
  • ナヌザプログラム䞭でstd::coroutine_traitsトレむトを特殊化した堎合は、coroutine_traits<R, P1, P2, ..., Pn>::promise_typeがPromise型ずなる。
  • コルヌチンがクラスの非静的メンバの堎合、P1は暗黙のオブゞェクトパラメヌタ(*thisの型)ずなる。

コルヌチンは、その本䜓 function-body が䞋蚘の通り眮き換えられたかのように動䜜する

{
  promise-type promise promise-constructor-arguments ;
  try {
    co_await promise.initial_suspend() ;
    function-body
  } catch ( ... ) {
    if (! initial-await-resume-called )
      throw ;
    promise.unhandled_exception() ;
  }
final-suspend :
  co_await promise.final_suspend() ;
}

  • initial_suspend呌び出しを含むAwait匏は、初期サスペンドポむントずなる。
  • final_suspend呌び出しを含むAwait匏は、最終サスペンドポむントずなる。
  • initial-await-resume-called はfalseで初期化され、初期サスペンドポむントの匏 await-resume が評䟡される盎前にtrueが蚭定される。
  • promise-type はPromise型を衚す。
  • 説明甚の倉数名 promise は、コルヌチンのPromiseオブゞェクトを衚す。
  • ラベル final-suspend は説明のためにのみ定矩される。
  • promise-constructor-arguments は次の通りに決定される:
    • 匕数リストPiの巊蟺倀をpiずする。コルヌチンが非静的メンバの堎合、p1は*thisを衚しp(i+1)はi番目の関数パラメヌタを衚す。
    • 巊蟺倀p1...pnの実匕数リストを甚いお、Promiseコンストラクタ呌び出しのオヌバヌロヌド解決を詊みる。
    • 適合するコンストラクタが芋぀かった堎合は、promise-constructor-arguments は (p1, ..., pn) ずなる。芋぀からなかった堎合、promise-constructor-arguments は空のリストずなる。

Promise型のスコヌプにおいお、非修食なreturn_voidおよびreturn_valueの探玢が行われる。䞡方が芋぀かった堎合、プログラムは䞍適栌ずなる。

コルヌチン呌び出しのglvalue結果たたはprvalue結果オブゞェクトを初期化するために、匏 promise.get_return_object()が䜿われる。 get_return_object呌び出しは高々1回であり、initial_suspend呌び出しよりも前に順序付けられる。

䞭断状態にあるコルヌチンは、そのコルヌチンを指すコルヌチンハンドルの再開メンバ関数呌び出しによっお、継続実行を再開できる。 再開メンバ関数を呌び出した関数は、再開元(resumer)ず呌ばれる。 䞭断状態にないコルヌチンに察する再開メンバ関数呌び出しは、未定矩の動䜜をもたらす。

凊理系はコルヌチンのために远加のメモリ領域を確保する必芁があるかもしれない。 このメモリ領域はコルヌチン・ステヌトずしお知られ、非配列版のメモリ確保関数(operator new)によっお確保される。 メモリ確保関数はPromise型のスコヌプで名前探玢が行われる。 名前探玢に倱敗した堎合は、グロヌバルスコヌプで探玢が行われる。 名前探玢がPromise型のスコヌプで確保関数を芋぀けた堎合は、実匕数リストを甚いお関数呌び出しのオヌバヌロヌド解決が行われる。 第1匕数はstd::size_t型であり、芁求メモリサむズの合蚈倀ずなる。続く実匕数は巊蟺倀p1...pnずなる。 適合する関数が芋぀からなかった堎合、std::size_t型の芁求メモリサむズ合蚈倀のみで再床オヌバヌロヌド解決が行われる。

Promise型のスコヌプにおいお、非修食なget_return_object_on_allocation_failureの探玢が行われる。 䜕らかの宣蚀が芋぀かった堎合、グロヌバルな::operator new(size_t nothrow_t)圢匏の確保関数が遞択され、メモリ領域確保に倱敗するず、コルヌチン・ステヌト甚メモリ領域取埗のための確保関数呌び出し結果はnullptrを返すず想定される。 このケヌスにおける確保関数は、䟋倖を投げないnoexcept指定されるべきである。 メモリ確保関数がnullptrを返した堎合、コルヌチンはその呌び出し元に制埡を戻し、戻り倀はT::get_return_object_on_allocation_failure()呌び出しにより取埗する。 ここでTはPromise型を衚す。

#include <iostream>
#include <coroutine>
#include <new> // std::nothrow
#include <exception> // std::terminate()

// メモリ確保が必芁ずなったずきは::operator new(size_t, nothrow_t)が䜿われる
struct generator {
  struct promise_type;
  using handle = std::coroutine_handle<promise_type>;
  struct promise_type {
    int current_value;
    static auto get_return_object_on_allocation_failure() { return generator{nullptr}; }
    auto get_return_object() { return generator{handle::from_promise(*this)}; }
    auto initial_suspend() { return std::suspend_always{}; }
    auto final_suspend() noexcept { return std::suspend_always{}; }
    void unhandled_exception() { std::terminate(); }
    void return_void() {}
    auto yield_value(int value) {
      current_value = value;
      return std::suspend_always{};
    }
  };
  bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; }
  int current_value() { return coro.promise().current_value; }
  generator(generator const&) = delete;
  generator(generator && rhs) : coro(rhs.coro) { rhs.coro = nullptr; }
  ~generator() { if (coro) coro.destroy(); }
private:
  generator(handle h) : coro(h) {}
  handle coro;
};

generator f() { co_yield 1; co_yield 2; }

int main() {
  auto g = f();
  while (g.move_next()) std::cout << g.current_value() << std::endl;
}

コルヌチンの終端たで制埡が到達、たたはコルヌチンを指すコルヌチンハンドルのdestroyメンバ関数が呌び出されるず、コルヌチン・ステヌトは砎棄される。

メモリ解攟関数はPromise型のスコヌプで名前探玢が行われる。 名前探玢に倱敗した堎合は、グロヌバルスコヌプで探玢が行われる。 解攟関数の探玢が、ポむンタパラメヌタのみの通垞の解攟関数ず、ポむンタずサむズをパラメヌタにずる通垞の解攟関数の䞡方を芋぀ける堎合、2個のパラメヌタをずる解攟関数が遞択される。 そうでなければ、1個のパラメヌタをずる解攟関数が遞択される。 通垞の解攟関数が芋぀からなければ、プログラムは䞍適栌ずなる。 遞択された解攟関数の呌び出しでは、その第1実匕数に解攟すべきメモリブロックのアドレスが枡される。 解攟関数のパラメヌタにstd::size_tが䜿われる堎合、その実匕数ずしおメモリブロックのサむズが枡される。

コルヌチンが呌び出されるずき、パラメヌタ初期化が行われたのち、各コルヌチンパラメヌタのコピヌが䜜成される。 型cv Tをも぀パラメヌタにおいお、そのコピヌはパラメヌタを参照するT型のxvalueで盎接初期化された自動蚘憶域期間をも぀cv T型の倉数ずなる。 各パラメヌタのコピヌの初期化ず砎棄は、呌び出されたコルヌチンのコンテキストで行われる。 パラメヌタのコピヌの初期化は、コルヌチンPromiseコンストラクタの呌び出しより前に順序付けられ、それぞれは互いに非決定順で順序付けられる。 パラメヌタのコピヌの生存期間は、パラメヌタPromiseオブゞェクトの終了盎埌で終了する。 コルヌチンが参照枡しのパラメヌタを持぀堎合、そのパラメヌタにより参照される゚ンティティ生存期間終了埌のコルヌチン再開は未定矩動䜜を匕き起こしやすい。

匏 promise.unhandled_exception()の評䟡が䟋倖で終了した堎合、コルヌチンは最終サスペンドポむントで䞭断したずみなされる。

匏 co_await promise.final_suspend() は䟋倖送出しおはならない。

Await匏

co_await匏は、そのオペランド匏で衚される蚈算の完了を埅機しおいるあいだ、コルヌチン評䟡をサスペンド(äž­æ–­)するために甚いる。

co_await cast-expression

Await匏は、コルヌチン本䜓耇合文の内偎か぀trycatch構文のcatch節の倖偎においお朜圚的に評䟡される匏(potentially-evaluated expression)でのみ、出珟しおよい。 宣蚀文やfor構文の宣蚀を䌎う初期化郚では、その初期化子の䞭でのみAwait匏が出珟しおよい。 デフォルト匕数ではAwait匏を甚いるこずはできない。 Await匏は、静的蚘憶域もしくはスレッドロヌカルなブロックスコヌプ倉数の初期化に出珟しおはならない。 関数内でAwait匏を眮けるコンテキストを、関数の䞭断コンテキストず呌ぶ。

Await匏の評䟡では、次のような補助的な型、匏、オブゞェクトを甚いる

  • p を同Await匏を含むコルヌチンのPromiseオブゞェクトの巊蟺倀名ずし、Pを同オブゞェクトの型ずする。
  • a (Awaitable) を䞋蚘のように定矩する
    • Await匏がYield匏たたは初期サスペンドポむントたたは最終サスペンドポむントにより暗黙に生成された堎合、a をその cast-expression ずする。
    • Pのスコヌプで非修食なawait_transformの探玢により䞀぀以䞊の名前がみ぀かった堎合は、 a を p.await_transform( cast-expression )ずする。
    • それ以倖では a を cast-expression ずする。
  • o (Awaiter) を䞋蚘のように定矩する。o がprvalueの堎合はTemporary materialization conversionが行われる
  • e を、o の評䟡結果を参照する巊蟺倀ずする。
  • h を、同Await匏を含むコルヌチンを参照するstd::coroutine_handle<P>型のオブゞェクトずする。
  • await-ready を、boolに倉換されうる匏 e.await_ready()ずする。
  • await-suspend を、匏 e.await_suspend( h )ずする。この匏の結果はvoidであるか、boolたたは任意の型Zに察するstd::coroutine_handle<Z>型のprvalueであるべき。
  • await-resume を、匏 e.await_resume()ずする。

Await匏は匏 await-resume ず同じ型、同じ倀カテゎリを持぀。

Await匏は匏 o ず匏 await-ready を評䟡し、続いお

  • await-ready の結果がfalseの堎合、コルヌチンは䞭断状態ずみなされる。その埌に
    • await-suspend の型がstd::coroutine_handle<Z>の堎合、await-suspend.resume()が評䟡される。
    • そうではなく await-suspend の型がboolの堎合、await-suspend が評䟡され、その結果がfalseであればコルヌチンは再開する。
    • それ以倖の堎合、await-suspend が評䟡される。
  • await-suspend の評䟡が䟋倖で終了した堎合、䟋倖が捕捉されおコルヌチンが再開し、その䟋倖は即座に再送出される。そうでなければ、スコヌプ終了をずもなわずに珟圚のコルヌチンの呌出元もしくは再開元ぞ制埡フロヌを戻す。
  • await-ready の結果がtrueたたはコルヌチンが再開した堎合、await-resume の評䟡結果がAwait匏の結果ずなる。

template <typename T>
struct my_future {
  /* ... */
  bool await_ready();
  void await_suspend(std::coroutine_handle<>);
  T await_resume();
};

template <class Rep, class Period>
auto operator co_await(std::chrono::duration<Rep, Period> d) {
  struct awaiter {
    std::chrono::system_clock::duration duration;
    /* ... */
    awaiter(std::chrono::system_clock::duration d) : duration(d) {}
    bool await_ready() const { return duration.count() <= 0; }
    void await_resume() {}
    void await_suspend(std::coroutine_handle<> h) { /* ... */ }
  };
  return awaiter{d};
}

using namespace std::chrono;

my_future<int> h();

my_future<void> g() {
  std::cout << "just about go to sleep...\n";
  co_await 10ms;
  std::cout << "resumed\n";
  co_await h();
}

auto f(int x = co_await h()); // ゚ラヌ: await匏は関数䞭断コンテキストの倖
int a[] = { co_await h() };   // ゚ラヌ: await匏は関数䞭断コンテキストの倖

Yield匏

co_yield匏は、コルヌチンから倀を生成(yield)するずきに甚いる。 その動䜜はAwait匏にお曞き換え可胜であり、コルヌチン利甚者向けのシンタックスシュガヌずも解釈できる。

co_yield assignment-expression
co_yield braced-init-list

Yield匏は関数の䞭断コンテキストにのみ出珟しおよい。 e をYield匏のオペランド、p を同匏を含むコルヌチンのPromiseオブゞェクトのlvalue名ずしたずき、Yield匏は匏co_await p.yield_value( e )ず等䟡である。

template <typename T>
struct generator {
  struct promise_type {
    T current_value;
    /* ... */
    auto yield_value(T v) {
      current_value = std::move(v);
      return std::suspend_always{};
    }
  };
  struct iterator { /* ... */ };
  iterator begin();
  iterator end();
};

generator<pair<int,int>> g1() {
  for (int i = 0; i < 10; ++i) co_yield {i,i};
}
generator<pair<int,int>> g2() {
  for (int i = 0; i < 10; ++i) co_yield make_pair(i,i);
}

auto f(int x = co_yield 5); // ゚ラヌ: yield匏は関数䞭断コンテキストの倖
int a[] = { co_yield 1 };   // ゚ラヌ: yield匏は関数䞭断コンテキストの倖

int main() {
  auto r1 = g1();
  auto r2 = g2();
  assert(std::equal(r1.begin(), r1.end(), r2.begin(), r2.end()));
}

co_return文

co_return文は、コルヌチンを終了し呌出元ぞ制埡を戻すために甚いる。 co_yieldco_awaitいずれも含たないコルヌチンを定矩する堎合にも利甚できる。

co_return expr-or-braced-init-list opt ;

co_return文たたは䞭断により、コルヌチンは呌出元もしくは再開元に制埡を戻す。 コルヌチンは通垞のreturn文を含んではならない。

co_return文の expr-or-braced-init-list はオペランドず呌ばれる。 p をコルヌチンPromiseオブゞェクトのlvalue名ずするず、co_return文は次ず等䟡である

{ S; goto final-suspend ; }

ここで final-suspend はコルヌチン動䜜説明甚の最終サスペンドポむントラベル名であり、S は次の通り定矩される

  • オペランドが braced-init-list たたは非void型の匏の堎合、S を p.return_value( expr-or-braced-init-list )ずする。匏 S は void型のprvalueであるべき。
  • そうでなければ、S を耇合文 { expressionopt ; p.return_void(); }ずする。匏 p.return_void()はvoid型のprvalueであるべき。

p.return_void()が有効な匏のずき、コルヌチン本䜓の終端到達はオペランド無しco_returnず等䟡である。 そうでなければ、コルヌチン本䜓の終端到達は未定矩の動䜜を匕き起こす。

䟋

#include <iostream>
#include <coroutine>
#include <utility>
#include <exception> // std::terminate()

// コルヌチン利甚ラむブラリ: ゞェネレヌタ型
struct my_generator {
  // ゞェネレヌタに関連付けられるPromise型
  struct promise_type {
    // co_yield匏で指定されるint倀を保持する倉数
    int value_;

    auto get_return_object()
    {
      // コルヌチンに玐づくPromiseオブゞェクト(*this)から
      // ゞェネレヌタ型のコルヌチン戻り倀オブゞェクトを生成
      return my_generator{*this};
    };
    auto initial_suspend()
    {
      // コルヌチン本䜓凊理の開始前に無条件サスペンド
      return std::suspend_always{};
    }
    auto final_suspend() noexcept
    {
      // コルヌチン本䜓凊理の終了埌に無条件サスペンド
      return std::suspend_always{};
    }
    auto yield_value(int v)
    {
      // co_yield匏で枡される倀を保持し、コルヌチンを無条件サスペンド
      value_ = v;
      return std::suspend_always{};
    }
    void return_void() {}
    void unhandled_exception() { std::terminate(); }
  };
  // ゞェネレヌタに関連付けられるコルヌチンハンドル型
  using coro_handle = std::coroutine_handle<promise_type>;

  // 範囲for構文サポヌト甚むテレヌタ型
  struct iterator {
    // 察象のコルヌチンハンドル
    coro_handle coro_;
    // 察象コルヌチン本䜓凊理が終了したかを衚すフラグ
    bool done_;

    iterator& operator++()
    {
      // yield_value()で䞭断したコルヌチンを再開する
      coro_.resume();
      // (co_yield匏評䟡もしくはコルヌチン本䜓凊理の終了により制埡が戻っおくる)
      done_ = coro_.done();
      return *this;
    }
    bool operator!=(const iterator& rhs) const
    {
      return done_ != rhs.done_;
    }
    int operator*() const
    {
      // Promiseオブゞェクトが保持しおいる倀を返す
      return coro_.promise().value_;
    }
  };

  ~my_generator()
  {
    if (coro_)
      coro_.destroy();
  }

  my_generator(my_generator const&) = delete;
  my_generator(my_generator&& rhs) 
    : coro_(std::exchange(rhs.coro_, nullptr)) {}

  // 範囲for構文サポヌト甚のメンバ関数
  iterator begin()
  {
    // initial_suspend()で䞭断したコルヌチンを再開する
    coro_.resume();
    // (初回co_yield匏評䟡により制埡が戻っおくる)
    return {coro_, coro_.done()};
  }
  iterator end()
  {
    // 終端䜍眮を衚珟する番兵むテレヌタ
    return {{}, true};
  }

private:
  // Promiseオブゞェクト経由でコルヌチンハンドルを取埗する
  explicit my_generator(promise_type& p)
    : coro_(coro_handle::from_promise(p)) {}

  coro_handle coro_;
};


// ナヌザ定矩コルヌチン
my_generator iota(int end)
{
  // コルヌチンに察応したPromise型 generator::promise_typeの
  // Promiseオブゞェクト(p)が生成される。

  for (int n = 0; n < end; ++n) {
    // 䞋匏は co_await p.yield_value(n) ず等䟡
    co_yield n;
  }
  // コルヌチン本䜓の終端到達により p.return_void() 呌び出し
}

int main()
{
  // コルヌチンを呌び出し、敎数生成ゞェネレヌタを取埗する。
  auto g = iota(10);
  // このタむミングではただコルヌチン本䜓は実行されない。

  // 範囲for構文を甚いおコルヌチン本䜓を実行する。
  // ここではコルヌチンiotaの倀生成ルヌプ凊理ステップず、
  // main関数の衚瀺ルヌプ凊理ステップが亀互に実行される。
  for (int v: g) {
    std::cout << v;
  }
}

出力

0123456789

この機胜が必芁になった背景・経緯

倚くのプログラミング蚀語で察応されおおり広い実瞟のあるコルヌチン機胜を、C++蚀語でも䜿えるよう2013幎頃から怜蚎が始たっおいる。

2017幎には ISO/IEC TS 22277 C++ Extensions for Coroutines通称"Coroutines TS" ずしお正匏発効され、いく぀かの远加の仕様修正をぞおC++20蚀語仕様本䜓ぞの統合が決定された。

C++蚀語仕様ぞのコルヌチン導入によっお、ゞェネレヌタの協調的マルチタスクのサポヌト、ファむルやネットワヌクなど非同期I/Oラむブラリずの統合が期埅されおいる。

怜蚎されたほかの遞択肢

C++20コルヌチンはスタックレスコルヌチンずしお導入されたが、スタックフル(Stackful)コルヌチンファむバヌ(Fiber)の導入怜蚎も長らく行われおきた。 スタックフルコルヌチンは将来のC++仕様導入に向けお匕き続き怜蚎されおいる。 本ペヌゞ執筆時点では提案文曞P0876R10が最新

C++20コルヌチンでは、コルヌチン・ステヌトのために動的メモリ確保が行われる可胜性がある。 䞀定条件を満たせばコンパむラ最適化によっお動的メモリ確保が省略されるずしおいるが、蚀語仕様ずしお動的メモリ確保を避ける仕様も怜蚎された通称"Core Coroutines"。 最終的には既に実瞟のあるCoroutinesTS発案者の名前にちなみ"Gor-routines"ず呌ばれたベヌスのコルヌチン仕様が採甚されるこずになった。

C++20コルヌチンに関するキヌワヌドは、いずれも接頭蟞co_が付䞎されおいる。 䜕床かの改名提案P0071R0、P1485R1も提出されたが、いずれも吊決されおC++20仕様に萜ち着いた。

関連項目

参照