#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?
CodePudding user response:
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;
}
CodePudding user response:
Modifying elements of a filter_view
in this way is specified as UB.
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.