I found some code that dereferences end() from an iota_view.
I asked the author about it and they pointed out that end() of an iota_view where Bound and W are the same type is iterator{bound} and operator* of iterator just returns its value. So this is not UB and is perfectly fine.
I've confirmed the points on cppreference (assuming that's all correct) and it seems to work: https://godbolt.org/z/Yv7YPhv3G
#include <ranges>
#include <iostream>
int main() {
auto range = std::views::iota(0, 5);
std::cout << *range.end() << std::endl;
return 0;
}
So is this legit? Feels weird to dereference an end().
Feels weird to dereference an
end().
As it should! It is always undefined behavior to dereference end(). A C++ range is defined by [begin(), end()) and is always half-open.
In the case of views::iota when you provide a bound, like views::iota(0, 5), then dereferencing end() will work just fine because it's basically just iterator{5} and there's nothing special about 5 that would make it not work (assuming the implementation doesn't add an assert).
But then there are plenty of other cases where dereferencing end() happens to be totally fine - for instance you could construct a string_view that's a prefix of a string - that string_view's end() is going to be a valid iterator within the string somewhere, so dereferencing would give you whatever char happens to be there (assuming the implementation doesn't add an assert).
But just because there are cases where it happens to work does not mean that in general it's a valid thing to do. Your inclination is correct: it should feel weird to dereference an end(). Because it's wrong. You should never do it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With