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

vu8f3i0k  于 2022-12-20  发布在  其他
关注(0)|答案(2)|浏览(142)
#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;
}

输出:

1
3
3
5
5

1
4 // Interesting...
3
5
5

您是否注意到第二个输出中的第二个4?它是如何结束的?这似乎是由于even.begin()在视图的第一次迭代期间被缓存,因此视图不再正确,因为无论*even.begin()的实际值如何,它总是以相同的元素开始。
这是否意味着视图不应该被重用呢?但是为什么要缓存呢?

kmb7vmvb

kmb7vmvb1#

以这种方式修改filter_view的元素被指定为UB。
[范围.过滤器.迭代器]/1:
允许修改filter_­view​::​iterator表示的元素,但如果结果值不满足筛选 predicate ,则会导致未定义的行为。
视图可以重用,但当然需要遵循规则。

qpgpyjmq

qpgpyjmq2#

even.begin()已在视图的第一次迭代期间被缓存,视图不再正确
是的,您理解正确-这就是filter_view的工作原理。
这是否意味着视图不应该被重用呢?但是为什么要缓存呢?
只要你不修改视图的结果,视图总是可以被重用的。这就是实现缓存的原因。你也可以修改结果,但是你必须小心。对于涉及 predicate 的视图(比如take_view),你的修改不允许改变 predicate 的结果。在你的例子中,如果你的修改是:

for (auto& element : even)
{
    element+=2;
}

相关问题