A coroutine is a function that can suspend its execution partway through (via co_await or co_yield) and resume later exactly where it left off, retaining its local state across the suspension. C++20 introduced this at the language level — the keywords and the underlying compiler transformation — but deliberately shipped no concrete, ready-to-use coroutine return types in the standard library itself, leaving that to be built by libraries or hand-written per project. std::generator, added in C++23, finally provides one standard, ready-to-use type specifically for the generator use case; other coroutine patterns (async tasks, for instance) still commonly rely on third-party libraries.
std::generator (C++23): the one ready-to-use standard coroutine type
co_yield suspends the coroutine and hands a value back to the caller, resuming from that exact point on the next iteration — the function's local state (i, in this case) is preserved across each suspension, just like Python's generators.
#include <generator> // C++23
std::generator<int> range(int start, int end) {
for (int i = start; i < end; i++) {
co_yield i; // suspends here, hands i back, resumes from here next time
}
}
int main() {
for (int n : range(1, 5)) {
std::cout << n << " "; // 1 2 3 4
}
}Why C++20 alone wasn't enough to just "use coroutines"
Before C++23's std::generator, writing even a simple generator required hand-authoring an entire promise_type class implementing several required member functions — this genuine complexity is why most C++20-era coroutine adoption went through third-party libraries rather than raw language features directly.
// A minimal hand-rolled coroutine return type needs, at minimum:
// - a nested promise_type
// - initial_suspend() / final_suspend()
// - return_void() or return_value()
// - yield_value() if using co_yield
// - unhandled_exception()
// — genuinely substantial boilerplate for even a simple generator, pre-C++23co_await: the mechanism behind async code
co_await suspends the current coroutine until the awaited operation completes, without blocking the underlying thread — conceptually similar to await in JavaScript or Python, though C++'s standard library still provides no built-in async task type, leaving that role to libraries.