Skip to content

Coroutines (C++20)

Beginning of the lesson

Coroutines (C++20)

Functions that can suspend, remember where they were, and resume later

Big Idea: An ordinary function runs from top to bottom and returns exactly once. A coroutine can suspend itself in the middle, hand control back to whoever called it, and later be resumed from the exact point it paused, with all its local variables still intact. C++20 adds three keywords — 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).

By the end you will be able to:
  • say precisely what makes a function a coroutine, and what the three co_ keywords do;
  • explain the coroutine frame, the promise_type, and coroutine_handle;
  • read a hand-written Generator that yields values lazily with co_yield;
  • see how co_await replaces 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 to co_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.

Naming trap: the coroutine 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.

Continue this lesson on YourAIZone

The full lesson has the complete explanation, worked examples, common mistakes, tests with instant checking and an AI tutor. Start right away as a guest, or create an account to keep your progress.

In the full lesson

  • The complete explanation
  • Worked examples
  • Common mistakes and how to avoid them
  • Test questions: 15
  • AI tutor that answers questions about the topic
  • Progress tracking for students, parents and teachers