Coroutines (C++20)
Functions that can suspend, remember where they were, and resume later
co_await, co_yield, co_return — that turn a normal-looking function into this pausable state machine. That single capability underlies two very different tools: lazy generators (produce values one at a time on demand) and asynchronous code without callbacks (write await-style sequential code that does not block a thread).
- say precisely what makes a function a coroutine, and what the three
co_keywords do; - explain the coroutine frame, the
promise_type, andcoroutine_handle; - read a hand-written
Generatorthat yields values lazily withco_yield; - see how
co_awaitreplaces callback chains with straight-line async code; - name the honest trade-offs: heap frames, thin C++20 library support, and lifetime traps.
1. What makes a function a coroutine
You do not declare a coroutine with a special keyword on the signature. Instead, a function becomes a coroutine if its body uses at least one of these three keywords:
co_await expr— possibly suspend until expr is ready, then continue (and optionally produce a result).co_yield value— produce one value to the caller and suspend; equivalent toco_await promise.yield_value(value).co_return value— finish the coroutine, optionally delivering a final result.
The compiler rewrites such a function into a state machine. Because control can leave and re-enter the body, the local variables cannot live on the ordinary call stack (the stack frame is gone the moment the function suspends). They live instead in a coroutine frame: a block of storage, usually allocated on the heap, that holds the parameters, the locals, and a small bookkeeping object called the promise.
promise_type has nothing to do with std::promise/std::future. It is a customization point the compiler talks to; you rarely touch it directly — a library type provides it.