Home > Software engineering >  Range transformations with stateful lambdas and std::views::drop
Range transformations with stateful lambdas and std::views::drop

Time:12-20

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.

  • Related