Consider standard algorithms like, say, std::for_each
.
template
Function for_each(InputIterator first
The standard explicitly requires the last
iterator to be reachable from the first
iterator. That means that by incrementing first
one should be able to eventually hit last
.
24.1 Iterator requirements
...
6 An iterator
j
is called reachable from an iteratori
if and only if there is a finite sequence of applications of the expression++i
that makesi == j
. Ifj
is reachable fromi
, they refer to the same container.7 Most of the library’s algorithmic templates that operate on data structures have interfaces that use ranges. A range is a pair of iterators that designate the beginning and end of the computation. A range
[i, i)
is an empty range; in general, a range[i, j)
refers to the elements in the data structure starting with the one pointed to byi
and up to but not including the one pointed to byj
. Range[i, j)
is valid if and only ifj
is reachable fromi
. The result of the application of functions in the library to invalid ranges is undefined.
This is explained by section 24.1 of the standard, "Iterator Requirements":
An iterator
j
is called reachable from an iteratori
if and only if there is a finite sequence of applications of the expression++i
that makesi == j
. Ifj
is reachable fromi
, they refer to the same container.…
Range
[i, j)
is valid if and only ifj
is reachable fromi
. The result of the application of functions in the library to invalid ranges is undefined.
So v.begin() + 3
is reachable from v.begin()
, but not the reverse. So [v.begin()+3, v.begin())
is not a valid range, and your call to for_each
is undefined.
The standard defines complexity constraints for the functions taking ranges. In the specific case of for_each
(25.2.4 in the C++ standard):
Complexity: Applies
f
exactlylast - first
times
So it's effectively a no-op in your example.
Does that mean that the following is technically valid? Or is it undefined? What can I realistically expect it to do?
No it is not. Your code would exhibit undefined behavior when for_each
increments the iterator and that iterator would be pointing to end
and there is nothing to dereference(Well, it is enough to get undefined behavior at this point, so there is no point talking about past end)!
The result is Undefined.
C++03 Standard: 25.1.1 For each and
C++11 Standard: 25.2.4 For each
states:
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);
1 Effects: Applies f to the result of dereferencing every iterator in the range [first, last), starting from first and proceeding to last - 1
While another section defines the valid range [first,last)
as:
C++03 Standard: 24.1 Iterator requirements and
C++11 Standard: 24.2.1 Iterator requirements
Para 7 for both:
Most of the library’s algorithmic templates that operate on data structures have interfaces that use ranges.A range is a pair of iterators that designate the beginning and end of the computation. A range [i, i) is an empty range; in general, a range [i, j) refers to the elements in the data structure starting with the one pointed to by i and up to but not including the one pointed to by j. Range [i, j) is valid if and only if j is reachable from i. The result of the application of functions in the library to invalid ranges is undefined.
Having remembered of reading this somewhere, just browsed through:
C++ Standard Library - A Tutorial and Reference - By Nicolai Josutils
This finds a mention in:
5.4.1 Ranges
The caller must ensure that the first and second arguments define a valid range. This is the case if the end of the range is reachable from the beginning by iterating through the elements. This means, it is up to the programmer to ensure that both iterators belong to the same container and that the beginning is not behind the end. If this is not the case, the behavior is undefined and endless loops or forbidden memory access may result.