Consider the following code (click here for godbolt):
#include <algorithm>
#include <ranges>
#include <vector>
int main() {
auto v = std::vector<short>{1, 2};
auto view = v | std::views::transform([] (auto i) { return static_cast<int>(i); });
auto it = view.begin() + 1;
auto prev_it = std::ranges::prev(it); //this one is fine
//auto prev_it = std::prev(it); //this one dies with an infinite loop
return *prev_it;
}
Main question: Calling std::prev
instead of std::ranges::prev
on the iterator makes gcc run into an infinite loop. This means there’s a compiler bug or the code calling std::prev
invokes undefined behaviour – which one is it?
Some thoughts on the latter: std::prev
requires a LegacyBidirectionalIterator, whereas std::ranges::prev
requires the concept bidirectional_iterator. From my understanding, the only difference between these two is (taken from the description of bidirectional_iterator):
Unlike the LegacyBidirectionalIterator requirements, the bidirectional_iterator concept does not require dereference to return an lvalue.
If this indeed means that calling std::prev
invokes undefined behaviour: Do basically all non-ranges algorithms invoke undefined behaviour when called with iterators of the new kind, as LegacyForwardIterator and forward_iterator share the same difference? Could the constraints for the old algorithms not be relaxed to the new iterator-kind, to avoid exactly this, as that would be still backwards-compatible?