缓存视图对象内部会导致UB?

Ale*_*104 3 c++ c++20 std-ranges

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

int main()
{
    std::vector<int> ints {1, 2, 3, 4, 5};
    auto isEven = [] (const auto& element)
    {
        return element % 2 == 0;
    };
    auto even = ints | std::views::filter(isEven);
    for (auto& element : even)
    {
        ++element;
    }
    for (const auto& element : ints)
    {
        std::cout << element << "\n";
    }
    std::cout << "\n\n";
    
    // Interesting things start further...
    for (auto& element : even)
    {
        ++element;
    }
    for (const auto& element : ints)
    {
        std::cout << element << "\n";
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

1
3
3
5
5


1
4 // Interesting...
3
5
5
Run Code Online (Sandbox Code Playgroud)

您注意到4第二个输出中的这一秒了吗?它是怎么到那里的?似乎发生的原因是even.begin()在视图的第一次迭代期间已被缓存,并且视图不再正确,因为它始终以相同的元素开头,无论 的实际值如何*even.begin()

这是否意味着视图不应该被重用?但为什么要缓存呢?

cpp*_*ner 6

以这种方式修改a的元素filter_view被指定为UB。

\n

[范围.过滤器.迭代器]/1 :

\n
\n

允许修改 a 表示的元素filter_\xc2\xadview\xe2\x80\x8b::\xe2\x80\x8biterator,但如果结果值不满足过滤谓词,则会导致未定义的行为。

\n
\n

视图可以重复使用,但是当然你需要遵守规则。

\n


Eug*_*ene 5

even.begin()在视图的第一次迭代期间已被缓存,并且视图不再正确

是的,你理解正确——这就是filter_view工作原理。

这是否意味着视图不应该被重用?但为什么要缓存呢?

只要不修改视图的结果,视图就始终可以重复使用。这就是实施缓存的原因。您也可以修改结果,但必须小心。对于涉及谓词的视图(就像take_view这样做),您的修改不允许更改谓词的结果。在您的示例中,如果您的修改是:

for (auto& element : even)
{
    element+=2;
}
Run Code Online (Sandbox Code Playgroud)