Go deeper — read the bookC++20 coroutines: a generator from scratch— runnable code & full walkthrough →

cpp20 · advanced

C++20 Coroutines — co_yield, co_await, co_return

C++20 coroutines let you write functions that can suspend and resume. Any function containing co_yield, co_await, or co_return is a coroutine — the compiler transforms it, allocating a heap frame to preserve local variables and the current suspend point across resume calls. co_yield v suspends and passes v to the caller via promise_type::yield_value. co_await expr suspends until the awaitable is ready — the mechanism for zero-overhead async I/O. co_return completes the coroutine and destroys the frame. The caller holds a coroutine_handle<> and calls .resume() to continue execution. The return type must define a nested promise_type with: get_return_object, initial_suspend, final_suspend, yield_value, return_void/return_value, and unhandled_exception. Coroutines give you async/await and lazy generators with no OS thread overhead.

🔑 Key line

A C++20 coroutine is any function containing co_yield, co_await, or co_return; the compiler allocates a heap frame to hold locals and the suspend point; co_yield suspends and passes a value to the caller, co_await suspends until an awaitable is ready, co_return ends the coroutine and destroys the frame.

The code

// 1. A generator coroutine (lazily yields integers)
Generator<int> range(int from, int to) {
for (int i = from; i < to; ++i)
co_yield i; // suspend, pass value to caller
} // co_return implicit at end
// 2. promise_type protocol (boilerplate inside Generator<T>)
struct promise_type {
int current_value;
auto yield_value(int v) {
current_value = v;
return suspend_always{};
}
auto get_return_object() {
return Generator{handle::from_promise(*this)};
}
auto initial_suspend() {
return suspend_always{};
}
auto final_suspend() noexcept {
return suspend_always{};
}
void return_void() {}
void unhandled_exception() {
std::terminate();
}
};
// 3. Caller side
auto gen = range(0, 3); // coroutine created, frame on heap, NOT running
gen.resume(); // runs until first co_yield → yields 0
int v = gen.value(); // 0
gen.resume(); // continues → yields 1
v = gen.value(); // 1
gen.resume(); // continues → co_return (done)
// 4. co_await — suspend until async result ready
Task<int> fetchData() {
int result = co_await asyncRead(); // suspend here
co_return result + 1;
}

What this lesson walks through

  1. 01What is a C++20 coroutine?
  2. 02Caller invokes coroutine — frame allocated on heap, NOT running
  3. 03gen.resume() → runs until co_yield, yields value 0 to caller
  4. 04gen.resume() again → yields 1, then 2, then done
  5. 05co_return — coroutine completes, frame destroyed
  6. 06co_await — suspend until an async result is ready
  7. 07promise_type protocol — the glue the compiler uses

A coroutine is a function that can suspend its execution and be resumed later. Unlike threads, suspension is cooperative and zero-cost: no OS context switch. The C++20 keywords are co_yield (suspend + pass value), co_await (suspend until awaitable), and co_return (finish).

See it animated — step by step, at your own pace

Unlock the full interactive walkthrough of C++20 Coroutines — co_yield, co_await, co_return and 100+ animated C++ interview lessons.

← Previous
C++20 Concepts & requires — Type Constraints Made Explicit
Next →
C++20 Ranges & views — Lazy Zero-Copy Pipelines