简体   繁体   中英

Caching inside view object leads to UB?

#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;
}

The output:

1
3
3
5
5


1
4 // Interesting...
3
5
5

Did you notice this second 4 in the second output? How did it end up there? It seems that happened due to even.begin() has been cached during the first iteration over the view, and the view is not correct anymore because of that as it always starts with the same element regardless of the actual value of *even.begin() .

Does that mean that views are not supposed to be reused? But why caching then?

Modifying elements of a filter_view in this way is specified as UB.

[range.filter.iterator]/1 :

Modification of the element a filter_view::iterator denotes is permitted, but results in undefined behavior if the resulting value does not satisfy the filter predicate.

Views can be reused, but of course you need to follow the rules.

even.begin() has been cached during the first iteration over the view, and the view is not correct anymore

Yes, you understood it correctly - that's how filter_view works.

Does that mean that views are not supposed to be reused? But why caching then?

Views can always be reused as long as you don't modify their results. That is why the caching is implemented. You can modify the results, too, but you have to be careful. For the views that involve a predicate (like take_view does), your modifications are not allowed to change results of the predicate. In your example, reusing the view would be fine if your modification was:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM