**Exploring the New C++20 Coroutines: A Beginner’s Guide**

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_suspend and final_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

` can replace manual generator structs: “`cpp #include std::generator natural_numbers(int n) { for (int i = 0; i < n; ++i) co_yield i; } “` ### 5. Performance Considerations – **Stack Allocation**: By default, coroutines allocate their promise object on the heap; consider `std::generator` or custom allocators for performance-critical paths. – **Inlining**: Small coroutines can be inlined by the compiler; use `[[nodiscard]]` and `noexcept` where appropriate. – **Exception Safety**: Unhandled exceptions propagate out of the coroutine; implement `unhandled_exception` in `promise_type`. ### 6. Common Pitfalls | Pitfall | Solution | |———|———-| | Forgetting to call `resume()` | Always resume or use range-based loops over generators. | | Returning values with `co_yield` without `promise_type` | Define `yield_value` in `promise_type`. | | Ignoring `final_suspend` return type | Use `std::suspend_always` to allow cleanup before destruction. | ### 7. Real-World Use Cases – **Lazy Evaluation**: Stream processing pipelines that generate values on-demand. – **Asynchronous I/O**: Network libraries can await sockets without callbacks. – **Cooperative Multitasking**: Lightweight tasks scheduled on a single thread. ### 8. Further Reading – “C++20 Coroutines” – The C++ Standard Committee’s design notes. – “Boost.Coroutine2” – A widely-used library providing coroutine primitives before C++20. – “Async Await in C++” – Practical tutorials on integrating coroutines with existing async frameworks. — By mastering C++20 coroutines, developers can write clearer, more maintainable asynchronous code while retaining fine-grained control over execution flow. Experiment with generators and `co_await` to see how coroutines can transform the way you write modern C++ applications.

发表评论