CodeBlanks Logo Icon CodeBlanks

co_yield

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

main.cpp
#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

main.cpp
#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

Question
Generator counter() { ____ 1; }
Answer
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

All lessons in Advanced