Generator<int> counter() {
for (int i = 0;; ++i) co_yield i; // suspends, yielding i
}
C++
Topics in this category
Casting & Conversions
Compilation & Linking
Concepts & Constraints
Concurrency
Const Correctness
Constexpr & Compile-Time
Coroutines
Enums & Enum Classes
Exception Handling
I/O Streams
Lambda Expressions
Memory Management
Modern C++
Move Semantics
Namespaces
OOP & Classes
Operator Overloading
Preprocessor & Macros
RAII & Smart Pointers
Ranges & Views
C++
Coroutines
A coroutine is a function that can suspend its execution and resume later, preserving its local state across suspensions. A function becomes a coroutine if its body uses co_await, co_yield, or co_return. Coroutines enable writing asynchronous or lazy/generator code in a sequential, readable style instead of callbacks or state machines.
Real-world example
An async network handler is written as a coroutine that co_awaits I/O, reading like straight-line code.
Concurrency
Coroutines
Coroutines
co_await suspends the coroutine until an awaited operation is ready, then resumes. co_yield suspends and produces a value to the caller (used for generators), resuming on the next request. co_return ends the coroutine, optionally returning a value. These keywords drive the suspend/resume behavior.
co_yield x; // produce a value, suspend
co_await task; // wait for async result
co_return r; // finish
Real-world example
A lazy sequence generator uses co_yield to produce one element per resumption on demand.
Coroutines
Concurrency
Coroutines
A regular function runs to completion in one invocation using a single stack frame that's destroyed on return. A coroutine can suspend and resume, so its state (locals, execution point) is stored in a separate 'coroutine frame' (often heap-allocated) that persists across suspensions until it completes or is destroyed. This lets it pause without blocking a thread.
Real-world example
Unlike a normal function, a coroutine pauses at co_await and lets the thread do other work meanwhile.
Memory Management
Concurrency
Coroutines
A generator is a function producing a sequence of values lazily, one at a time, on demand. With coroutines, co_yield suspends and hands back a value each time the consumer asks for the next, resuming where it left off. This avoids computing or storing the whole sequence, enabling infinite or expensive sequences to be consumed incrementally.
Generator<int> fib() {
int a=0,b=1;
while (true) { co_yield a; auto t=a; a=b; b=t+b; }
}
Real-world example
An infinite Fibonacci generator yields numbers lazily so the consumer takes only as many as needed.
Ranges & Views
Coroutines
Coroutines
A coroutine's return type must provide a promise_type controlling its behavior (initial/final suspend, how co_yield/co_return/exceptions are handled, and how the return object is built). An awaitable (with await_ready/await_suspend/await_resume) defines what co_await does. coroutine_handle is a low-level handle to resume/destroy the coroutine. The standard library provides little out of the box before C++23's std::generator, so libraries or hand-written types supply these.
struct promise_type {
auto get_return_object();
std::suspend_always initial_suspend();
std::suspend_always final_suspend() noexcept;
void return_void(); void unhandled_exception();
};
Real-world example
A library author writes a Task type with a promise_type to integrate coroutines with its event loop.
Templates
Coroutines
Coroutines
Coroutine frames are typically heap-allocated (though the compiler may elide the allocation—HALO optimization—when the lifetime is bounded and visible). Suspension/resumption is cheap versus thread context switches. Concerns include the allocation cost, potential for many live frames, and ensuring awaited objects/captured references outlive suspension. They excel at high-concurrency I/O without a thread per task.
Real-world example
An async server handles thousands of connections with coroutines instead of thousands of OS threads, saving memory.
Memory Management
Concurrency
Coroutines
Coroutines suit asynchronous I/O (network/file operations written sequentially), lazy generators/sequences, cooperative multitasking, state machines, and streaming/pipeline processing. They make code that would otherwise use callbacks, futures chaining, or explicit state machines read like ordinary sequential code.
Real-world example
An asynchronous download is written as a coroutine that co_awaits each chunk, avoiding nested callbacks.
Concurrency
Coroutines
Coroutines
std::generator (C++23) is a standard library coroutine return type for writing generators—so you can co_yield values without hand-writing a promise_type and iterator machinery. It provides a ready-made, range-compatible lazy sequence type, making the common generator use case simple and portable, unlike C++20 where you needed a custom or third-party type.
std::generator<int> squares(int n){
for (int i=0;i<n;++i) co_yield i*i;
}
Real-world example
With std::generator, producing a lazy sequence needs only co_yield, no boilerplate coroutine plumbing.
Ranges & Views
Modern C++
Coroutines
Pitfalls include dangling references: a coroutine that captures or references locals/parameters by reference can access them after the caller's frame is gone across a suspension; passing parameters by reference to a coroutine that outlives them is dangerous. Also, forgetting to keep the coroutine handle alive, or awaiting objects that don't outlive the suspension, cause use-after-free. Prefer passing owned/by-value data into coroutines.
// dangerous: reference parameter may dangle after suspension
Task process(const std::string& ref); // prefer by value/owning
Real-world example
A coroutine referencing a temporary argument crashes after suspension; passing by value fixes it.
Memory Management
Coroutines
Coroutines
Threads are OS-scheduled, run truly in parallel (on multiple cores), and each has its own stack with relatively high memory and context-switch cost. Coroutines are cooperatively scheduled within a thread—they suspend/resume at explicit points, are extremely lightweight, and don't provide parallelism by themselves. Coroutines handle concurrency (many tasks) efficiently; threads provide parallelism.
Real-world example
One thread runs thousands of coroutines cooperatively, versus the high cost of thousands of OS threads.
Concurrency
Coroutines
Coroutines