Consider the following code using the ranges library (from c++20)
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> inputs{1, 2, 3, 4, 5, 6};
auto square_it = [](auto i) {
std::cout << i << std::endl;
return i * 2; };
auto results = inputs | std::views::transform(square_it) | std::views::filter([](auto i){ return i % 3 == 0; });
for(auto r : results) {
// std::cout << r << std::endl;
}
}
The cout
in the square
function is to log when the square
function is called by the ranges library. This code prints
1
2
3
3
4
5
6
6
The question is, why are values that match the filter's predicated are printed twice?
I have seem this code in a presentation in CppCon 2020, where the presenter explains why this happens. According to him, filter iterates until its predicate is satisfied (and of course if needs to call transform
each time). Then filter
stops and it is ready to be iterated on. After that the actual iteration is started and a value is read from filter
, which then calls transform
again a second time for the same input.
It is not clear to me why this is necessary. Since ranges::views
compute values lazily and every view operation pulls data from the one before it, why can't filter just pass the value to whoever is after it in the pipeline as soon as it finds a match?