1

I tried an experiment:

#include <iostream>
#include <vector>

int main(void) {
  std::vector<int> a{1, 2, 3};
  std::vector<int>::iterator b = a.begin();
  std::vector<int>::iterator c = a.end();
  std::vector<int>::iterator d = b - 1;
  std::vector<int>::iterator e = c + 1;
  std::cout << true << std::endl;
  std::cout << (d < b) << std::endl;
  std::cout << (e > c) << std::endl;
  return 0;
}

It outputs:

1
1
1

But someone told me that it is behavior undefined for deque, so what do you think? Thank you!

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
acgtyrant
  • 1,721
  • 1
  • 16
  • 24
  • Dereferencing an iterator before `begin()`, or dereferencing `end()` or an iterator past `end()` results in undefined behavior. (Basically you are accessing memory outside the array. Anything could happen -- for a deque you are likely to get a segfault, for a vector you're likely to just get nonsense values.) – rlbond Aug 03 '15 at 16:22
  • 1
    Containers with random-access iterators *might* let you get away with comparing the iterators, but it certainly won't work for non-contiguous containers, and if you try to do anything that invokes a dereference of the iterator that will be undefined too. – Cory Kramer Aug 03 '15 at 16:24
  • 2
    I think that the question is not about dereferencing of out-of-bounds iterators, but about arithmetic operations on them. Is there UB in `auto it = a.end() + 1;`? Language lawyer required! – Ivan Aksamentov - Drop Aug 03 '15 at 16:34
  • 2
    @Drop - `a.end() + 1` is undefined, as `a.end()` could be at the end of memory (or a memory segment). – Bo Persson Aug 03 '15 at 16:38
  • @BoPersson Okay, let's keep assuming that iterator is implemented as a pointer (we don't know that, but it could be). Than `auto it = mem_max + 1` is pretty well defined operation of integer addition. As long as I don't dereference it, where's UB? – Ivan Aksamentov - Drop Aug 03 '15 at 16:43
  • 1
    @Drop - for one thing, the addition might overflow. If it does not, the resulting address might be outside the space allocated for the process. Some hardware traps for just holding an illegal address in an address register. But on x86 Linux or Windows, probably nothing bad happens. – Bo Persson Aug 03 '15 at 16:46
  • 1
    Only the fact that your code compiles and gives meaningful results does not mean that this is not undefined behaviour. Undefined means anything can happen. In the worst case what will happen is that you get the result that you would expect until one day your code fails and does something really ugly. – 463035818_is_not_an_ai Aug 03 '15 at 16:53
  • 1
    @Drop: How iterators are implemented has nothing at all to do with whether certain behaviours on them under certain conditions have well-defined semantics. – Lightness Races in Orbit Aug 03 '15 at 16:54
  • @LightnessRacesinOrbit I understand this. We was considering an isolated case where iterator is a pointer and whether an arithmetic operation on it is defined (which is an interesting topic by itself) – Ivan Aksamentov - Drop Aug 03 '15 at 16:59

3 Answers3

3

No.

Both d and e are "singular iterators" because they point neither to an element in a sequence, or to the "one-past-the-end" pseudo-element.

And you can barely do anything with singular iterators:

[C++11: 24.2.1/5]: Just as a regular pointer to an array guarantees that there is a pointer value pointing past the last element of the array, so for any iterator type there is an iterator value that points past the last element of a corresponding sequence. These values are called past-the-end values. Values of an iterator i for which the expression *i is defined are called dereferenceable. The library never assumes that past-the-end values are dereferenceable. Iterators can also have singular values that are not associated with any sequence. [ Example: After the declaration of an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular value of a pointer. —end example ] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation.

Note that, in particular, you cannot perform arbitrary comparisons on them.

I was pretty sure that even evaluating a.begin() - 1 or a.end() + 1 were UB, but I can't find any evidence of that right now.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Nice answer. What about arithmetics `auto it = a.end() + 1;`? Or even better: `(it - 1) == a.end()`? – Ivan Aksamentov - Drop Aug 03 '15 at 16:47
  • @Drop: What about it? – Lightness Races in Orbit Aug 03 '15 at 16:50
  • Is it defined? Are we guaranteed to receive back `a.end()` after adding and then subtracting same integer value from it? – Ivan Aksamentov - Drop Aug 03 '15 at 16:51
  • @Drop: I addressed that in my answer. – Lightness Races in Orbit Aug 03 '15 at 16:53
  • @Drop It is undefined behaviour for pointers (see standard 5.7.4), for standard library containers I can't actually find anything about it in the standard but [What if I increment an iterator by 2 when it points onto the last element of a vector?](http://stackoverflow.com/a/1057788) indicating that it's undefined with a quote of nicolai josuttis "The C++ Standard Library" that's the best I can get. – AliciaBytes Aug 03 '15 at 16:59
  • @RaphaelMiedl: Meh, not very convincing! – Lightness Races in Orbit Aug 03 '15 at 17:02
  • @LightnessRacesinOrbit sadly seems to be the best I can do, I searched again and found nothing in the standard and all links on the web seem to point to nicolai josuttis quote, so no way to prove if it is or isn't UB for me. I'd say the end result is that it's strongly discouraged though since we can't prove that it isn't UB and the risk is there. – AliciaBytes Aug 03 '15 at 17:33
2

Not just for deque but this is undefined for any container (only random access containers will actually allow this to compile though).

Mark B
  • 95,107
  • 10
  • 109
  • 188
0

The behaviour on using either d or e is undefined, as you suspected.

You are allowed to test c for equality with a.end(), but note that the behaviour on any dereferencing it is undefined.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483