Coroutines were introduced in C++20 as a powerful language feature that simplifies asynchronous and lazy computations. They allow functions to suspend and resume execution without manual state machine management, providing a cleaner syntax for generators, streams, and cooperative multitasking.
1. The Basics of Coroutines
A coroutine is defined by adding the co_ prefix to the return type:
#include <coroutine>
#include <iostream>
struct task {
struct promise_type {
task get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};
task example() {
std::cout << "Start\n";
co_return; // End of coroutine
}
Key concepts:
promise_type– the coroutine’s internal state container.initial_suspendandfinal_suspend– control suspension points at the start and end.co_return/co_yield/co_await– suspend/resume points.
2. Using co_yield to Create Generators
Coroutines can produce a sequence lazily:
#include <coroutine>
#include <iostream>
struct int_generator {
struct promise_type {
int current;
int_generator get_return_object() { return {std::coroutine_handle <promise_type>::from_promise(*this)}; }
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
std::suspend_always yield_value(int value) {
current = value;
return {};
}
};
std::coroutine_handle <promise_type> h;
int operator()() {
h.resume();
return h.promise().current;
}
bool done() const { return !h || h.done(); }
};
int main() {
int_generator gen = []() -> int_generator {
for (int i = 0; i < 5; ++i)
co_yield i * i; // Yield squares
}();
while (!gen.done())
std::cout << gen() << ' ';
std::cout << '\n';
}
Output:
0 1 4 9 16
3. Asynchronous Awaiting with co_await
Coroutines can await asynchronous operations. The standard library provides std::future and std::shared_future as awaitable types:
#include <future>
#include <chrono>
#include <iostream>
std::future <int> async_add(int a, int b) {
return std::async(std::launch::async, [=] {
std::this_thread::sleep_for(std::chrono::seconds(1));
return a + b;
});
}
task add_and_print() {
int result = co_await async_add(3, 4);
std::cout << "Result: " << result << '\n';
}
4. Integrating Coroutines with Standard Library
C++20 adds std::ranges::coroutine, std::generator, and other utilities to ease coroutine usage. For instance, `std::generator