Since the std::generator
is making it into CPP23, I am playing around with MSVC's incomplete version.
However, I notice that it seems lose exactly one yield
when used with std::views::take
. Here is the example:
#include <iostream>
#include <ranges>
#include <experimental/generator>
std::experimental::generator<int> GeneratorFn(void) noexcept
{
co_yield 1;
co_yield 2;
co_yield 3;
co_yield 4;
co_yield 5;
co_yield 6;
co_yield 7;
co_yield 8;
co_yield 9;
co_return;
}
int main(int argc, char** args) noexcept
{
auto Ret = GeneratorFn();
for (auto&& i : Ret | std::views::take(2))
std::cout << i << '\n';
for (auto&& i : Ret | std::views::take(3))
std::cout << i << '\n';
for (auto&& i : Ret | std::views::take(4))
std::cout << i << '\n';
}
The output of this code would be
1
2
4
5
6
8
9
and clearly, the 3
and 7
is missing. It seems like std::views::take
drops the last value the generator
yields.
Is this normal and to be expected in the formal version of C 23?
(Try online: https://godbolt.org/z/v6MModvaz)
CodePudding user response:
std::generator
is an input_range
, its begin()
does not guarantee equality-preserving:
auto Ret = GeneratorFn();
std::cout << *Ret.begin() << "\n"; // 1
std::cout << *Ret.begin() << "\n"; // 2
When your first for-loop finishes, Ret
's iterator has already incremented to the value 3
. When you apply views::take
to Ret
in the second for-loop, this will call Ret
's begin
again, and the iterator return by begin
will be the next value 4
.
If you don't want to discard the value of the end iterator, you can reuse the last end iterator like this
auto Ret = GeneratorFn();
auto c = std::views::counted(Ret.begin(), 2);
for (auto i : c)
std::cout << i << '\n';
for (auto i : std::views::counted(c.begin(), 3))
std::cout << i << '\n';
for (auto i : std::views::counted(c.begin(), 4))
std::cout << i << '\n';