5

In the accepted answer to "Iterator to last element of std::vector using end()--" @barry states:

Note that if vector::iterator is just T* (which would be valid), the first form above is ill-formed. The second two work regardless, so are preferable.

referring to his code:

std::vector<int>::iterator it = --container.end();
std::vector<int>::iterator it = container.end() - 1;
std::vector<int>::iterator it = std::prev(container.end());

This opinion is disputed in the comments, however without a clear resolution. So that's my question: what exactly is the semantic difference between the first and the second? And would the answer be different for iterators over structures other than vector?

ByteEater
  • 885
  • 4
  • 13
  • The last comment by Mark Ransom _"rvalues are temporary values..."_ seems to answer the question here. Do you have a question or some confusion about that comment? – Drew Dormann Oct 04 '21 at 22:58
  • So the answer is that indeed, the first line can cause undefined behaviour? And that happens when the iterator type (of any container) is a type which doesn't overload the `--` operator? If so, I'd accept an answer to this effect. – ByteEater Oct 04 '21 at 23:09
  • As far as I understand, for a `std::vector` all 3 possibilities are fines. I don't see the problem with `--container.end()`. `end()` returns an iterator and `--` decrement it. As a general rule, any syntax that is valid with raw pointer should also be valid with `std::vector`. – Phil1970 Oct 04 '21 at 23:12
  • @Phil1970 `std::vector` can use raw pointers for its iterator type, in which case `--` won't work. – François Andrieux Oct 05 '21 at 01:04
  • @ByteEater If an iterator is not reversible, then trying to use `--` won't compile. – François Andrieux Oct 05 '21 at 01:04
  • @ByteEater The first line will either work as expected, or fail to compile, depending on the implementation. It won't be Undefined Behavior for well implemented containers. – François Andrieux Oct 05 '21 at 01:05

1 Answers1

10

For any standard library container, the member function end() returns an r-value. It's a "temporary" until you assign it to a variable.

The decrement operator -- is not required to work on r-value iterators. You would be modifying a temporary, which C++ historically has taken measures to avoid.

Therefore, --container.end() might compile on your standard-conforming C++compiler. But it might not.

std::prev(container.end()) will work on every standard-conforming compiler.


To review:

  • --container.end() may not compile. It is up to the implementation.
  • container.end() - 1 will only compile if the container uses random-access iterators.
  • std::prev(container.end()) will always compile.

All three forms will produce the same result, if they compile.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • It gives much relevant information, thanks. You're clearly knowledgeable in this, so could you, please, make your answer fully adequate to my question and add these missing pieces: – ByteEater Oct 05 '21 at 00:27
  • What kind of error is `--container.end()`? A compile-time error (if so, is it about wrong types, and why just might – doesn't the standard dictate these unambiguously?)? Undefined behaviour? Something else? – ByteEater Oct 05 '21 at 00:28
  • Is `container.end() - 1` always equivalent to `std::prev(container.end())`? If not (I imagine you could define an iterator type for your own container type which overloads `-` or `--` and have whatever semantics you implement), is it guaranteed for standard library containers? – ByteEater Oct 05 '21 at 00:31
  • (This comment added after your first edit.) You wrote: "Therefore, `--container.end()` might compile on your standard-conforming C++compiler. But it might not." So, if I understand correctly, the standard says `end` should return an iterator but leaves the exact type as implementation-defined? If so, does it require that if `--container.end()` compiles, its semantics is the same? – ByteEater Oct 05 '21 at 00:42
  • (This comment added after your second edit.) Does it also require that if a random-access iterator is used (as I suppose, mandatory for `vector`, but optional (implementation-defined) for some other containers), `container.end() - 1` has the same semantics? – ByteEater Oct 05 '21 at 00:43
  • Drew, Is `container.end() - 1` valid if the vector is empty? – chux - Reinstate Monica Oct 05 '21 at 02:59
  • Does there is a compiler on which the first line does not compile? Maybe if one would try to compile pre C++ 11 STL with post C++ 11 compiler? I was not aware that -- operator was not working on r-value. Probably because, I never write such expression in practice. – Phil1970 Oct 06 '21 at 00:05