it's my first time digging into the new <ranges>
library and I tried a little experiment combining std::views::transform
with a stateful lambda and 'piping' the resulting range to std::views::drop
:
#include <iostream>
#include <ranges>
#include <vector>
using namespace std;
int main() {
auto aggregator = [sum = 0](int val) mutable
{
return sum = val;
};
vector<int> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout << "Expected:\n";
int sum = 0;
for (int val : data) {
cout << (sum = val) << ' ';
}
cout << '\n';
cout << "Transformation:\n- - - ";
for (int val : data | views::transform(aggregator) | views::drop(3)) {
cout << val << ' ';
}
cout << '\n';
}
The output was:
Expected:
1 3 6 10 15 21 28 36 45 55
Transformation:
- - - 4 9 15 22 30 39 49
Now, the difference between each expected and actual output is 1 2 3 = 6. I am guessing it is a result of the lazy evaluation of ranges that causes std::views::drop
to disregard the first three transformations.
Is there a way for me to force the evaluation of the aggregator
functor for the three elements I drop? Or are stateful lambdas and ranges considered incompatible?
CodePudding user response:
transform_view
is required to be a pure function. This is codified in the regular_invocable
concept:
The invoke function call expression shall be equality-preserving and shall not modify the function object or the arguments.
This is important to allow transform_view
to not lie about its iterator status. Forward iterators, for example, are supposed to allow multipass iteration. This means that the value at each iterator position within the range must be independent of any other iterator position. That's not possible if the transformation functor is not pure.
Note that all predicate
functors are also regular_invocable
. So this also applies to things like filter_view
and take_while_view
.
Note that the algorithm transform
does not have this requirement.