Question
Is it undefined behavior to directly remove elements from the underlying range of filter_view?
For the following code:
void foo(std::set<int>& s) {
for (int val : s | std::views::filter([](int i) { return i > 0; })) {
s.erase(val - 3);
}
}
This code is not intended to perform any meaningful task; it's only for illustrative purposes. Here, I use the range adaptor std::views::filter
to filter out all positive numbers from s
and remove the corresponding val - 3
elements.
This code involves erasing elements from the container while iterating over it. For std::set
, erasing an element does not invalidate iterators other than those pointing to the erased element. Therefore, if I convert the above code to an equivalent iterator form, it should not include undefined behavior:
void bar(std::set<int>& s) {
for (int val : s) {
if (val > 0) {
s.erase(val - 3);
}
}
}
However, I am not sure if foo
contains undefined behavior because the standard imposes additional semantic constraints on some concepts in the ranges library, such as requiring constrained expressions to be equality-preserving. I'm not sure if foo
violates these semantic requirements.
Additionally, range.filter.iterator explicitly allows modifying the underlying elements through iterators from filter_view
as long as the modified elements still satisfy the predicate. But the standard does not specify what happens when directly modifying the underlying range as in foo
.
Is foo
undefined behavior due to these modifications, or is it safe under the current C++ standard?