1

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?

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
theejazz
  • 41
  • 5
  • 3
    `views::transform` requires `F` must be [`regular_invocable`](https://en.cppreference.com/w/cpp/concepts/invocable) (which guarantees *equality preservation*), which means that using state lambda will be undefined behavior. – 康桓瑋 Dec 20 '21 at 07:09
  • Thanks @康桓瑋, that clears up the confusion! – theejazz Dec 20 '21 at 07:41

1 Answers1

4

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    Any reason why they don't call it pure function instead of regular invocable? Maybe it's just me, but when I hear pure function I know instantly what that means; for regular invocable I have to look it up. – bolov Dec 20 '21 at 07:46
  • Thanks! That definitely answers my question. It's interesting that while `transform_view` must be a pure function, my compiler didn't warn me that it isn't. – theejazz Dec 20 '21 at 07:49
  • @theejazz: Because your compiler can't tell whether it is a pure function or not. – Nicol Bolas Dec 20 '21 at 14:14