co_yield
- C++ course
- Advanced
- Lesson 70/83
- 5 min
Learn value generation with co_yield.
What is co_yield?
co_yield lets a coroutine produce a value and suspend. Each co_yield v; expression yields v to the outside and pauses the coroutine. When resume() is called from outside, the coroutine continues where it left off.
This is the basis of writing a generator. The promise type must have a yield_value(v) member. Example: co_yield 1; co_yield 2;. The first resume yields 1, the second resume yields 2. This way you can write infinite or lazy sequences with a single function.
Generator promise
A Generator type contains a promise_type with a yield_value member. Each co_yield v; calls yield_value(v) and the value is stored.
Example: std::suspend_always yield_value(int v) { value_ = v; return {}; }. Because it returns suspend_always, the coroutine suspends. If initial_suspend also returns suspend_always, the coroutine does not start until the first resume. This way values are produced lazily, on demand. With handle.resume() you advance to the next yield; with handle.promise().value_ you read the value.
Practice Produce numbers with co_yield
Write co_yield 1; and co_yield 2; in the body of the counter coroutine. The resume and std::cout calls in main are already there; do not touch them.
Starter code
#include <iostream>
#include <coroutine>
struct Generator {
struct promise_type {
int value_;
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 {}; }
std::suspend_always yield_value(int v) { value_ = v; return {}; }
void return_void() { }
void unhandled_exception() { }
};
std::coroutine_handle<promise_type> handle;
};
Generator counter() {
// Produce 1 and 2 with co_yield
}
int main() {
auto g = counter();
g.handle.resume();
std::cout << g.handle.promise().value_;
g.handle.resume();
std::cout << g.handle.promise().value_;
return 0;
}Sample solution
#include <iostream>
#include <coroutine>
struct Generator {
struct promise_type {
int value_;
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 {}; }
std::suspend_always yield_value(int v) { value_ = v; return {}; }
void return_void() { }
void unhandled_exception() { }
};
std::coroutine_handle<promise_type> handle;
};
Generator counter() {
co_yield 1;
co_yield 2;
}
int main() {
auto g = counter();
g.handle.resume();
std::cout << g.handle.promise().value_;
g.handle.resume();
std::cout << g.handle.promise().value_;
return 0;
}Expected output
12
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 What does co_yield do?
- It finishes the coroutine completely
- It restarts the coroutine
- It throws an error
- It produces a value and suspends the coroutine Correct answer
Explanation: co_yield produces a value and suspends the coroutine; it is resumed with resume.
2 the co_yield value
Generator counter() { ____ 1; }
Generator counter() { co_yield 1; }
Explanation: co_yield 1; produces the value and suspends.
3 Match each member to its role
| Term | Meaning |
|---|---|
yield_value |
Stores the value |
initial_suspend |
Start behaviour |
final_suspend |
Finish behaviour |
resume() |
Resumes |