与 C++20 中的简单有状态 lambda 相比,`co_yield` 的特殊值是什么?

What's the special value of `co_yield` in contrast to a simple stateful lambda in C++20?

来自著名的 C++ 协程库(在源文件 generator.hpp 中搜索“Don't allow any use of co_await inside the generator coroutine.”),以及我自己的实验,我知道使用 co_yield 的协程不能同时使用 co_await

既然使用 co_yield 的生成器必须是同步的,那么使用 co_yield 相对于简单的有状态 lambda 有什么优势?

例如:

#include <iostream>

generator<int> g()
{
    for (auto i = 0; i < 9; ++i)
    {
        co_yield i;
    }
}

int main()
{
    auto fn_gen = [i = 0] mutable { return i++; };

    // Lambda way
    for (auto i = 0; i < 9; ++i)
    {
        std::cout << fn_gen() << std::endl;
    }

    // co_yield way
    for (auto i : g())
    {
        std::cout << i << std::endl;
    }
}

co_yield与 C++20 中的简单状态 lambda 相比有什么特殊价值?

请查看更新后的 MWEhttps://godbolt.org/z/x1Yoen7Ys

在更新的示例中,在同一个协程中使用 co_awaitco_yield 时,输出完全出乎意料。

对于内部状态和代码最少的普通生成器,小型仿函数或 lambda 就可以了。但是随着您的生成器代码变得越来越复杂并且需要更多的状态,它变得不那么好。您必须在仿函数类型或 lambda 说明符中添加更多成员。函数内部的代码越来越大。等等

在最极端的情况下,基于 co_yield 的生成器可以向外界隐藏 所有 其实现细节,只需将其定义放在 .cpp 中文件。有状态仿函数不能隐藏其内部状态,因为它的状态是外部世界必须看到的类型的成员。避免这种情况的唯一方法是通过类型擦除,例如 std::function。在这一点上,你基本上没有比使用 co_yield.

此外,co_await 可以与 co_yield 一起使用。 Cppcoro 的 generator 类型显式管理它,但 cppcoro 不是 C++20。你可以写任何你想要的生成器,那个生成器可以 .

事实上,您可以制作异步生成器,有时您可以立即产生一个值,有时您可以通过一些异步进程安排值的可用性。调用异步生成器的代码可以 co_await 从中提取值,而不是将其视为仿函数或迭代器对。