if your data's sequential, creating an iterator in C++ is as simple as returning a begin and end pointer, and will be optimized away by any level other than O0
But iterating over pointers is once again optimized with lots of undefined behavior at the corners. So you are replacing one source of undefined behavior with another.
Replacing undefined behavior at the program-level with undefined behavior written and tested as part of the standard library, usually vendored and distributed in concert with the compiler, seems like an obvious net-positive to me.
Of course a basic iteration between begin()/end() will never contain out of range elements, but neither will valid increment between two integers. No need for iterators in that case either.
Say I want to do something fancy, like getting element number 11 from an array.
With an integer index I can pass 11, with random access iterators I can use begin() + 11.
Now my array only has five elements. So I check.
11 < 5? Comparison valid, successfully avoided a crash.
begin() + 11 < end() ? How where the rules for pointer comparison again, something about within an allocation and one past the end?
> something about within an allocation and one past the end?
Yeah, I forgot about that. So I agree there is some subtly which is likely to catch beginners.
Your example could safely be:
if (std::distance(begin, end) > 5)
Another approach I would recommend is to write a `guarded_advance` which takes an integer and the end pointer.
Also note that the situation you are describing is still a little unusual because the baseline assumption is it takes linear time to advance an iterator by more than 1 increment.
> but neither will valid increment between two integers. No need for iterators in that case either.
The purpose of an iterator is to abstract data structure access. The coordinate inside a complex data structure may not be representable by an integer.
> but you trust it to optimize all that garbage away?
Yep, if you learn about compilers you learn what kind of optimizations are easy and hard for them to make. The kind that just flattens a few levels of nested definitions are easier.
You don't trust your compiler to optimize the sane trivial C case, but you trust it to optimize all that garbage away?