为什么C ++的范围是"transform->"?过滤器"调用两次转换以匹配过滤器谓词的值? [英] Why C++ ranges "transform -> filter" calls transform twice for values that match the filter's predicate?

查看:98
本文介绍了为什么C ++的范围是"transform->"?过滤器"调用两次转换以匹配过滤器谓词的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用ranges库(来自c ++ 20)考虑以下代码

Consider the following code using the ranges library (from c++20)

#include <iostream>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> inputs{1, 2, 3, 4, 5, 6};

    auto square_it = [](auto i) {
        std::cout << i << std::endl;
        return i * 2; };

    auto results = inputs | std::views::transform(square_it) | std::views::filter([](auto i){ return i % 3 == 0; });

    for(auto r : results) {
        // std::cout << r << std::endl;
    }
}

square 函数中的 cout 用于记录范围库调用 square 函数时的情况.此代码会打印

The cout in the square function is to log when the square function is called by the ranges library. This code prints

1
2
3
3
4
5
6
6

问题是,为什么与过滤器的谓词匹配的值会被打印两次?

我在CppCon 2020的演示文稿中似乎看到了这段代码,演示者解释了为什么这样做发生.据他介绍,过滤器会迭代直到其谓词得到满足(当然,如果每次都需要调用 transform ,则必须进行过滤).然后 filter 停止,可以对其进行迭代了.之后,开始实际的迭代,并从 filter 读取值,然后再次对同一输入再次调用 transform .

I have seem this code in a presentation in CppCon 2020, where the presenter explains why this happens. According to him, filter iterates until its predicate is satisfied (and of course if needs to call transform each time). Then filter stops and it is ready to be iterated on. After that the actual iteration is started and a value is read from filter, which then calls transform again a second time for the same input.

我不清楚为什么这是必要的.由于range懒惰地计算值,并且每个元素都从其前一个元素提取数据,所以为什么过滤器不能在找到匹配项后立即将值直接传递给管道中后面的任何人?

It is not clear to me why this is necessary. Since ranges compute values lazily and every element pulls data from the one before it, why can't filter just pass the value to whoever is after it in the pipeline as soon as it finds a match?

推荐答案

为什么一旦找到匹配项,过滤器就不能立即将值传递给管道中紧随其后的任何人?

why can't filter just pass the value to whoever is after it in the pipeline as soon as it finds a match?

由于在迭代器模型中,定位和访问是不同的操作.您可以使用 ++ 定位迭代器;您可以使用 * 访问迭代器.这是两个不同的表达式,它们在两个不同的时间求值,导致两个不同的函数调用产生两个不同的值( ++ 产生一个迭代器, * 产生一个引用).

Because in the iterator model, positioning and accessing are distinct operations. You position an iterator with ++; you access an iterator with *. These are two distinct expressions, which are evaluated at two distinct times, resulting in two distinct function calls that yield two distinct values (++ yields an iterator, * yields a reference).

过滤迭代器为了执行其迭代操作,必须访问其基础迭代器的值.但是,该访问不能传递给 ++ 的调用者,因为该调用者仅要求放置迭代器,而不是获取其值.定位迭代器的结果是一个新的迭代器值,而不是存储在该迭代位置中的值.

A filtering iterator, in order to perform its iteration operation, must access the values of its underlying iterator. But that access cannot be communicated to the caller of ++ because that caller only asked to position the iterator, not to get its value. The result of positioning an iterator is a new iterator value, not the value stored in that iterated position.

因此没有人可以将其退回.

So there's nobody to return it to.

您不能真正将定位延迟到访问之后,因为用户可能会多次定位迭代器.我的意思是,您可以在理论上通过存储这种增量/减量的数量来实现该目标.但这增加了迭代器实现的复杂性.尤其是因为解决这种延迟的定位可以通过对另一个迭代器或哨兵进行测试这样的简单操作来完成,这应该是O(1)操作.

You can't really delay positioning until after accessing because a user might reposition the iterator multiple times. I mean, you could implement it that way in theory by storing the number of such increments/decrements. But this increases the complexity of the iterator's implementation. Especially since resolving such delayed positioning can happen through something as simple as testing against another iterator or sentinel, which is supposed to be an O(1) operation.

这仅仅是对迭代器模型的限制,因为它同时具有位置和值.迭代器模型被设计为指针的抽象,其中迭代和访问是不同的操作,因此它继承了这种机制.存在将迭代和访问捆绑在一起的替代模型,但是它们不是标准库迭代的工作原理.

This is simply a limitation of the model of iterators as having both position and value. The iterator model was designed as an abstraction of pointers, where iteration and access are distinct operations, so it inherited this mechanism. Alternative models exist where iteration and access are bundled together, but they're not how standard library iteration works.

这篇关于为什么C ++的范围是"transform->"?过滤器"调用两次转换以匹配过滤器谓词的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆