22

I have a std::vector and I want the iterator to the last element in the vector; I will be storing this iterator for later use.

NOTE: I want an iterator reference to it, not std::vector::back. Because I want to be able to compute the index of this object from the std::vector::begin later on.

The following is my logic to get the iterator to the last element:

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

Since std::vector::end has O(1) time complexity, is there a better way to do this?

Galik
  • 47,303
  • 4
  • 80
  • 117
Quark
  • 399
  • 1
  • 2
  • 11
  • 1
    I don't see anything wrong. So what's the question? – DeiDei May 04 '16 at 02:17
  • 3
    You need `--container.end()` instead of `container.end()--`. Also you need to make sure the container isn't empty first, or you'll have undefined behavior. – Mark Ransom May 04 '16 at 02:23
  • aha, I see by doing `std::vector::iterator it = container.end()--` I will get the `end` iterator then decrements the iterator – Quark May 04 '16 at 02:31
  • What else are you going to do with the iterator, Apart from computing the index to the last element? If nothing else, you may just store the index. Either way, the iterator or index might be invalid by the time you actually use them. – xiaofeng.li May 04 '16 at 02:36
  • @LukeLee I will be storing the object and the iterator to the object in a map. I want to be able to look up the object in O(1) and get the index. the index would change as the content of the vector can be removed and added – Quark May 04 '16 at 02:39
  • 1
    If you are messing with the vector's contents in a way that invalidates the index, then that will certainly invalidate the iterator too. – T.C. May 04 '16 at 07:12
  • @T.C. I will be syncing the vector and the map; so anything that is removed from the vector will be removed from map. also i.e `vector = [ 5,1,4,2,3]` if I get an iterator to the end which is `3`, then remove `1`. I can still use the iterator from `3` to get to `5`(the front) with out a problem. – Quark May 04 '16 at 17:44
  • @quark How would the iterator magically know there's one less item to subtract? – Barry May 04 '16 at 19:35
  • Any operation that invalidates an index invalidates the iterator corresponding to said index. (`erase`, for instance, invalidates every iterator at or after the point of the erase.) Using such an iterator results in undefined behavior. – T.C. May 04 '16 at 19:37
  • I rolled back the last edit because changing the question invalidated some of the answers. – Galik Dec 12 '18 at 11:40

3 Answers3

44

I think you mean either:

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

You're unintentionally just returning end(). But the problem with all of these is what happens when the vector is empty, otherwise they're all do the right thing in constant time. Though if the vector is empty, there's no last element anyway.

Also be careful when storing iterators - they can get invalidated.

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

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    The first one is invalid if the iterator is a pointer. The other two are fine. – T.C. May 04 '16 at 06:59
  • And the second one is restricted to random access containers. – MSalters May 04 '16 at 08:33
  • 2
    @MSalters The question is about `std::vector` – Barry May 04 '16 at 10:23
  • 1
    @T.C. why would the first be invalid? Decrementing pointers is a common operation. – Mark Ransom May 04 '16 at 15:48
  • @MarkRansom could you expand of the rvalue issue? I used --container.end() to get the last element and it works. – Quark May 04 '16 at 18:43
  • 3
    @quark rvalues are temporary values, and it makes no sense to modify them; that's why the modifying operators `++` and `--` are disallowed. See [What exactly is a R-Value in C++?](http://stackoverflow.com/questions/9406121/what-exactly-is-a-r-value-in-c). But if the temporary is a class type, the operator isn't applied directly but is converted to a `operator--` call, which is allowed on an rvalue - see [Why myClassObj++++ doesn't incur a compile error : '++' needs l-value](http://stackoverflow.com/questions/6692037/why-myclassobj-doesnt-incur-a-compile-error-needs-l-value-just-as-bu). – Mark Ransom May 04 '16 at 20:34
17

You have rbegin that does what you need

cplusplus reference

auto last = container.rbegin();
dau_sama
  • 4,247
  • 2
  • 23
  • 30
14

The way you are doing it will give you the wrong iterator because post increment will not change the value until after the assignment.

There is always this:

auto it = std::prev(container.end());

Remember to check first that the container is not empty so your iterator exists in a valid range.

Galik
  • 47,303
  • 4
  • 80
  • 117