P2881 proposes a new way to build iterators, similar to generators but with less performance overhead and better nesting (and hence composability):
struct generator123
{
auto operator()(auto&& sink) const
{
std::control_flow flow;
// With P2561: sink(1)??;
flow = sink(1);
if (!flow) return flow;
// With P2561: sink(2)??;
flow = sink(2);
if (!flow) return flow;
return sink(3);
}
};
int main() {
for (int x : generator123()) // x iterates 1, 2, 3
{
std::print("{}\n", x);
if (x == 2)
break;
}
}
While the benefits are undisputable, it’s quite verbose without P2561 and I feel C++ is starting to have too many different iterator models.
What do you think?
One of the authors has a nice presentation about pitfalls of ye olde for loops, which was probably a big motivator too: https://www.youtube.com/watch?v=95uT0RhMGwA
Although it looks like a nice proposal, I don’t think that it’s really workable. The major issue is that an additional scope is introduced, contrary to how for-loops normally work.
Say you throw an exception in the loop-body, it may be a surprise if generator123 handles the exception and there is no indication in the code that your exception will go through another scope.
And I just looked at the proposal and the 3 suggestions how to deal with exceptions and none of the proposals are good. Even worse: the operator() would act differently through compiler magic and depending on context.
I guess the syntax has to change, it looks pretty right now, but I don’t think it can stay that way.