Skip to content

Coroutines (Basics) (C++20)

In a Nutshell

A language mechanism that allows functions to suspend (suspend) mid-execution and resume (resume) later -- the foundational infrastructure for implementing lazy generators, asynchronous I/O, state machines, and other patterns.

#include <coroutine> (coroutine support library)

Core API Quick Reference

OperationSignatureDescription
Coroutine handlecoroutine_handle<promise_type>Type-erased coroutine handle, used to resume/destroy
Suspendco_await expr;Suspends the current coroutine, waiting for expr to complete
Yield valueco_yield expr;Suspends and returns a value to the caller
Returnco_return expr;Final return from the coroutine
Promise typestruct promise_typeType that customizes coroutine behavior (must be defined in the return type)
Initial suspend pointsuspend_always initial_suspend()Whether to immediately suspend when the coroutine starts
Final suspend pointsuspend_always final_suspend() noexceptWhether to suspend when the coroutine ends (noexcept is required)
Return objectget_return_object()Creates the object returned to the caller

Minimal Example

cpp
// Standard: C++20
#include <coroutine>
#include <iostream>

struct Generator {
    struct promise_type {
        int current_value;
        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{}; }
        auto yield_value(int v) { current_value = v; return std::suspend_always{}; }
        void return_void() {}
        void unhandled_exception() {}
    };
    using handle = std::coroutine_handle<promise_type>;
    handle coro;
    ~Generator() { if (coro) coro.destroy(); }
    bool next() { coro.resume(); return !coro.done(); }
    int value() { return coro.promise().current_value; }
};

Generator counter() {
    for (int i = 0; i < 3; ++i)
        co_yield i;
}

int main() {
    auto gen = counter();
    while (gen.next())
        std::cout << gen.value() << " "; // 0 1 2
}

Embedded Applicability: Medium

  • Stackless coroutines: when suspended, state is stored in a heap-allocated coroutine frame, keeping memory overhead manageable
  • Well-suited for implementing embedded async I/O, event loops, state machines, and similar patterns, replacing callback hell
  • Coroutine frames are heap-allocated by default, but can be redirected to static memory pools via custom operator new
  • C++20 provides only the language mechanism and minimal library support; practical high-level abstractions (e.g., std::generator) require C++23
  • Compiler support still has known ICE (internal compiler error) issues; thorough testing is needed for production use

Compiler Support

GCCClangMSVC
121419.28

See Also


Some content adapted from cppreference.com under CC-BY-SA 4.0 license

Built with VitePress