A C++ coroutine is a stackless function capable of suspending execution and yielding control back to its caller, with the ability to be resumed later. Unlike regular subroutines that strictly follow a Last-In-First-Out (LIFO) execution model and destroy their stack frame upon returning, coroutines maintain their state (local variables, parameters, and execution pointers) across suspensions via a dynamically allocated coroutine state. A function is implicitly deduced as a coroutine by the compiler if its body contains at least one of the following keywords:Documentation Index
Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt
Use this file to discover all available pages before exploring further.
co_await: Suspends execution until an awaited computation completes.co_yield: Suspends execution and yields a value to the caller.co_return: Completes the coroutine execution and returns a final value (or void).
main function, constexpr functions, constructors, and destructors.
The Coroutine Machinery
C++ does not provide built-in coroutine types likeTask or Generator. Instead, it provides a low-level framework consisting of three primary components that interact to manage the coroutine’s lifecycle:
1. The Promise Object
Manipulated from inside the coroutine. It acts as the communication channel between the coroutine and the caller. It dictates the coroutine’s behavior upon initialization, suspension, exception handling, and completion. The compiler resolves the promise type using std::coroutine_traits<ReturnType, Args...>. While the default implementation of this trait looks for a nested ReturnType::promise_type, developers can specialize std::coroutine_traits to adapt types that do not have a nested promise_type (such as standard library types or third-party classes).
2. The Coroutine Handle (std::coroutine_handle)
Manipulated from outside the coroutine. It is a non-owning pointer to the compiler-generated coroutine state. The caller uses the handle to resume execution (handle.resume()) or explicitly destroy the coroutine frame (handle.destroy()).
3. The Awaitable and Awaiter
Objects that dictate the semantics of a suspension point. When co_await <expr> is evaluated, <expr> evaluates to an Awaitable. The compiler then converts this Awaitable into an Awaiter (either via promise_type::await_transform(<expr>), an operator co_await() overload, or by the Awaitable already being a valid Awaiter). The resulting Awaiter must implement three methods:
await_ready(): Returns a boolean. Iftrue, suspension is skipped.await_suspend(handle): Called immediately after suspension. Can returnvoid,bool, or anothercoroutine_handleto transfer control.await_resume(): Called when the coroutine is resumed. Its return value is the result of theco_awaitexpression.
Syntax and Structural Implementation
To write a coroutine, you must define a return type that resolves to a valid promise type. Because the return type typically manages the lifecycle of thestd::coroutine_handle, it must strictly adhere to the Rule of Five to prevent undefined behavior such as double-freeing the coroutine state.
Execution Flow
When a coroutine is invoked, the compiler injects boilerplate code to manage the state machine. The exact sequence of operations is:- Allocation: The compiler allocates the coroutine state (typically on the heap, though the compiler may optimize this away via Heap Allocation Elision if the lifetime is strictly scoped).
- Initialization: Function parameters are copied/moved into the coroutine state.
- Promise Construction: The
promise_typeobject is constructed inside the coroutine state. - Return Object Creation:
promise.get_return_object()is called. The result is kept as a local variable to be returned to the caller later. - Initial Suspend:
co_await promise.initial_suspend()is executed. If it suspends (e.g., returnsstd::suspend_always), control returns to the caller, yielding the return object. - Execution: The caller resumes the coroutine via the handle. The coroutine body executes until it hits a
co_awaitorco_yield. - Completion: Upon hitting
co_return,promise.return_void()orpromise.return_value()is called. - Final Suspend:
co_await promise.final_suspend()is executed. It is highly recommended to suspend here so the caller can retrieve final values from the promise before the state is destroyed. - Destruction: The coroutine state is destroyed, either implicitly if it falls off the end without a final suspend, or explicitly via
handle.destroy().
Master C++ with Deep Grasping Methodology!Learn More





