c++generatorstdstd-rangesc++23

Why can't I `zip` a lvalue `generator`?


The following simple program compiles and works:

std::generator<int> get_ints(int n)
{
    for (int i{0}; i < n; ++i)
        co_yield i;
}

int main(int argc, char* argv[])
{
    std::vector<float> floats { 0.0f, 1.0f, 2.0f, 3.0f };

    for (auto [i, f] : std::views::zip(get_ints(4), floats))
        std::println("{}: {}", i, f);
}

However, if I instead store the generator in a variable and attempt the same, it does not compile:


std::generator<int> get_ints(int n)
{
    for (int i{0}; i < n; ++i)
        co_yield i;
}

int main(int argc, char* argv[])
{
    std::vector<float> floats { 0.0f, 1.0f, 2.0f, 3.0f };
    auto ints = get_ints(4);

    for (auto [i, f] : std::views::zip(ints, floats))
        std::println("{}: {}", i, f);
}

From my understanding this fails, because std::ranges::viewable_range<std::generator<int>&> evaluates to false, while std::ranges::viewable_range<std::generator<int>&&> evaluates to true. But why is the first case forbidden? I know that generator is not a range, but it behaves like one and every other range type that I tested (although I only tested a few) satisfies this constraint when passed as an lvalue reference. So why makes generator special in this case?


Solution

  • std::generator is a move-only range with a deleted copy constructor, you need to move it into the zip_view:

    for (auto [i, f] : std::views::zip(std::move(ints), floats))
        std::println("{}: {}", i, f);