使用有状态 lambda 和 std::views::drop 的范围转换

Range transformations with stateful lambdas and std::views::drop

这是我第一次深入研究新的 <ranges> 库,我尝试了一个小实验,将 std::views::transform 与有状态的 lambda 相结合,'piping' 结果范围为 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';
}

输出为:

Expected:
1 3 6 10 15 21 28 36 45 55 
Transformation:
- - - 4 9 15 22 30 39 49

现在,每个预期输出和实际输出之间的差异是 1 + 2 + 3 = 6。我猜这是惰性计算范围的结果导致 std::views::drop 忽略前三个转换.

有什么方法可以强制对我删除的三个元素的 aggregator 仿函数求值?还是有状态的 lambda 和范围被认为是不兼容的?

transform_view 必须是纯函数。这被编入 the regular_invocable concept:

The invoke function call expression shall be equality-preserving and shall not modify the function object or the arguments.

这对于允许 transform_view 不对其迭代器状态撒谎很重要。例如,前向迭代器应该允许多遍迭代。这意味着范围内每个迭代器位置的值必须独立于任何其他迭代器位置。如果转换仿函数不纯,那是不可能的。

请注意,所有 predicate functors 也是 regular_invocable。所以这也适用于 filter_viewtake_while_view.

注意算法transform没有有这个要求。