【问题标题】:C++20-ranges: reuse of a filter viewC++20-ranges:重用过滤视图
【发布时间】:2021-01-20 13:09:51
【问题描述】:

看不懂下面代码的结果(用g++-10编译的C++20):

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

int main() {
    using namespace std;
    
    auto v = vector<int>{0,1,2,3,4,5,6,7};
    auto r = v | views::filter([](int n) {return n % 3 == 0;});
    ranges::generate(r,[]() { return 2; });
    auto s = v | views::filter([](int n) {return n % 3 == 0;});
    
    cout << "v: ";
    for(auto x: v) cout << x << " ";
    cout << "\n";

    cout << "r: ";
    for (auto x: r) cout << x << " " ;
    cout << "\n";

    cout << "s: ";
    for (auto x: s) cout << x << " " ;
    cout << "\n";
}

在我的机器上,结果是:

v: 2 1 2 2 4 5 2 7 
r: 2 
s: 

我理解第一行 (v) 和第三行 (s):在 v 中,可被 3 整除的整数被替换为 2(第一行),因此之后不再有整数可以被 3 整除。因此第三行是空的。

但是为什么第二行(r)显示唯一值2,和第三行不一样???

如果我在 range::generate 中将 2 替换为 3,那么结果是正常的:

v: 3 1 2 3 4 5 3 7 
r: 3 3 3 
s: 3 3 3

也许原因是微不足道的,但我在文档中没有找到任何原因...

【问题讨论】:

    标签: c++ c++20


    【解决方案1】:

    你违反了[range.filter.iterator]/1:

    允许修改 filter_­view​::​iterator 表示的元素,但如果结果值不满足过滤谓词,则会导致未定义的行为。

    您的谓词是可被 3 整除的元素,并且您将所有这些都修改为 2 - 这不满足谓词。那是未定义的行为。不要那样做。

    在您的后续示例中,您正在修改元素,但它们仍然满足谓词 - 所以这很好。


    出现这种情况的原因是filter 必须缓存其begin() 迭代器以满足摊销O(1) 常量要求。最终发生的是,在generate 期间,filter 查找满足谓词的第一个元素(在本例中是第一个元素,0)。该结果被缓存。

    然后您将该值覆盖为2,它不再能被3 整除,但filterbegin() 仍然引用它。这就是它被打印出来的原因。

    当您创建一个 filter 时,该begin() 尚未缓存,因此不存在此类问题。

    【讨论】:

    • 非常感谢。即使有谷歌请求,我也没有设法找到这个文档......这是一个悲伤的答案,因为我所做的是我在发现范围时首先想到的事情之一:修改根据某些条件选择的容器的条目。什么是好方法?在上面的上下文中使用replace_if?更一般地说,我正在寻找一种表达generate_if的好方法。
    • reference site 根本不清楚哪些“约束算法”可以与“各种视图”一起使用。诸如我随机重新发现的不兼容应该以更清晰的方式呈现。还有其他众所周知的不兼容性吗?对于半初学者来说,一个很好的参考会很有趣:c++draft 对我来说从范围开始有点困难。
    • @DamienSimon 是的,草稿不是供用户参考的,而是作为实施的参考。我同意有一种很好的方式来呈现这类信息会很好,因为它非常微妙。
    猜你喜欢
    • 2021-05-23
    • 1970-01-01
    • 2022-01-13
    • 2019-05-23
    • 2021-01-04
    • 2021-12-15
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多