Suppose we have an array named
int *ptr = std::end(data);
To be able to tell an "empty" container/sequence from one with elements in it.
If a container is empty, a pointer to its first element would be one past the end, the same as the "end" location.
NULL pointer could also have been used, but since iterators are necessarily pointers, the
NULL would not apply. The default value for the iterator could also be used. In generic programming, how would the "default" be determined uniformly across all types (bear in mind pre C++98 implementations here)?
Not all sequences/containers, are contiguous in memory, any attempt to use a comparison such as
operator < would not be suitable. The use of equality (or inequality), operators
!= is required, so a single point (or element) is needed to indicate the last element; the "one past the end" solves that.
For uniformity, the "end" is chosen as one past the end; it solves a lot of the issues and brought power to the STL. The half closed interval has become the norm in C++.
On a side note, the ranges library and techniques shines another light on the matter, but this was not around when the foundation for the STL was laid out.
Another side note; not all sequences correlate to a container, some sequences that are required to be iterated over are only a portion of the original container or sequence, the half closed interval offers a uniform technique to access the sequence, independently of where it is in the original container/sequence.