C++2a(C++20)コルーチンにはジェネレータ実装を容易にするco_yield
式が導入されるが、動作仕様的にはco_await
式のシンタックスシュガーとなっている。
#include <coroutine> #include <iostream> #include <utility> #define MIMIC_CO_YIELD 1 #if MIMIC_CO_YIELD // yield式相当を表現する値保持クラス template <typename T> struct yield { T value; }; #endif template <typename T> struct generator { struct promise_type { T value_; auto get_return_object() { return generator(*this); } auto initial_suspend() noexcept { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } #if MIMIC_CO_YIELD // 式co_await yield{v}に対応するカスタイマイズポイント auto await_transform(yield<T>&& bag) { value_ = std::move(bag.value); return std::suspend_always{}; } #else auto yield_value(T x) { value_ = std::move(x); return std::suspend_always{}; } #endif void return_void() {} void unhandled_exception() { throw; } }; using coro_handle = std::coroutine_handle<promise_type>; generator(generator const&) = delete; generator(generator&& rhs) : coro_(rhs.coro_) { rhs.coro_ = nullptr; } ~generator() { if (coro_) coro_.destroy(); } bool next() { return coro_ ? (coro_.resume(), !coro_.done()) : false; } T& value() { return coro_.promise().value_; } private: generator(promise_type& p) : coro_(coro_handle::from_promise(p)) {} coro_handle coro_; }; generator<int> f() { #if MIMIC_CO_YIELD // co_await式を利用 co_await yield{1}; co_await yield{2}; #else // 通常のco_yield式 co_yield 1; co_yield 2; #endif } int main() { auto g = f(); while (g.next()) { std::cout << g.value() << std::endl; } }
2022-01-13追記:co_await
とco_yield
では演算子の優先順位が異なるため*1、いつでも両者を相互変換できるわけではない。co_await
は単項演算子と同じく優先順位が高く、co_yield
は代入演算子と同じく優先順位が低い。*2
C++2a WD(n4861) 7.6.17/p1より一部引用。
A yield-expression shall appear only within a suspension context of a function (7.6.2.3). Let
e
be the operand of the yield-expression andp
be an lvalue naming the promise object of the enclosing coroutine (9.5.4), then the yield-expression is equivalent to the expressionco_await p.yield_value(e)
.
(snip)
関連URL