A common example of strange behaviour with views::filter:
#include <iostream>
#include <ranges>
#include <vector>
int main ()
{
using namespace std;
auto ml = [](char c) // ml = make lambda (always accepts / transforms to 1)
{
return [c](int) {cout << c; return 1;};
};
vector<int> vec = {1};
auto view = vec
| views::transform (ml('T'))
| views::filter (ml('F'));
// use view somehow:
return *view.begin();
}
Which outputs TFT (note the extra T). demo
We must know that:
auto view = vec
| views::transform (ml('A'))
| views::filter (ml('B'));
...is just syntax sugar for:
auto view = views::filter(views::transform(vec, ml('A')), ml('B'));
Problem explained:
Having implemented a few mock versions of views::filter, it seems the issue is:
- Unlike other iterators,
filter::iteratordoes its work duringoperator ++(searching for an accepted value) operator *is designed to extract the value, but withfilter::iteratorthe work has already been done and lost (we don't need redo the search for an accepted value, but we do need to re-calculate it)- We can't store the result because of the constant-time copy constraint for views (the value could be an array)
To explain this in a picture we'll represent the process of iterating over:
view = container | Transform | Filter1 | Filter2 | Filter3
(apologies for the ugly diagram) circles represent the work being done Pr(F3) for Print F3 (the work done in the mock example):
We can see that if we combine only filters or only transforms then we don't repeat the work - the issue is having filters above the transforms (above in the diagram).
In worst-case we get [where n(x) number of x]:
n(iterations) = n(filters) * n(transforms)
when we'd expect n(filters) + n(transforms)
We can see this parabolic vs linear growth with this example.
Question
Sometimes the transforms need to be done before we can determine what to filter, so how to avoid the extra work-load? This seems important as views are designed to iterate over containers, which is bottle-neck territory.
Is there a way to use std::views for situations like this without the huge slow-down described above?
