-3

As I read that using containers' insert() or erase() method can invalidate the container and therefore it's better to not cache the end of a container using iterators before performing the insert () or erase() operation, I am confused about why only the last position of a container is invalidated but not the 1st one. (No advice is told in book that we should not cache an iterator referring the begin() of a container).

It may seem a dumb question but doesn't "invalidate" mean a total change of addresses for elements in the container? How can we be sure that the address of beginning of container remains always the same?

DevSolar
  • 59,831
  • 18
  • 119
  • 197
  • 1
    Paragraphs, capitalization? Proper spacing, perhaps? – sashoalm Feb 04 '15 at 15:37
  • 2
    "...only the last position of a container is invalidated." - *What container? – WhozCraig Feb 04 '15 at 15:38
  • "How can we be sure?" -- because that's what the standard states. Because it does, implementors have to respect that condition. (Technically, the begin() iterator does not *need* to change for insert() or erase(), and thus it is more efficient to do so anyway.) Note that the statement isn't true for all containers anyway. `std::map::erase()` only invalidates iterators of the *erased* element, for example. – DevSolar Feb 04 '15 at 15:40
  • 1
    `std::vector v; v.resize(v.capacity()+1, 1);` `v.begin()` *is* invalidated (and will almost certainly be different in practice). – BoBTFish Feb 04 '15 at 15:40
  • @DevSolar Where does the standard state that? The begin iterator can be invalidated, just like any other iterator. **Edit**: Oh I see, there's some fuzzy "beginning of the container" concept at play. – juanchopanza Feb 04 '15 at 15:41
  • You are confusing the `begin()` iterator, which *can* be invalidated, with the address of the container itself, which cannot really change. – juanchopanza Feb 04 '15 at 15:43
  • 1
    The book probably also didn't say that you shouldn't cache the iterator for the third element. Don't treat lack of warnings as approval. – molbdnilo Feb 04 '15 at 15:44
  • @juanchopanza: I find clear documentation on iterator validity e.g. [here](http://en.cppreference.com/w/cpp/container/vector/erase) or (even though I hesitate to link there) [here](http://www.cplusplus.com/reference/map/map/insert/). Would you care to give an example where the standard states that `begin()` gets invalidated on a `insert()` or `erase()` operation? -- But I see where my statement could be read to be ambiguous. **Always check the documentation on the function you are using.** Which iterators are invalidated and which aren't is *documented*, and for a reason... – DevSolar Feb 04 '15 at 15:49
  • @DevSolar `v.erase(v.begin());` and any call to `v.insert` that causes a re-allocation. – juanchopanza Feb 04 '15 at 15:53
  • @juanchopanza: Yep, that's the one case where it actually does, because `std::vector::erase()` is **documented** to "invalidate iterators and references *at or after the point of the erase*". But that's neither what the OP meant, nor what *I* meant in my initial comment: We can be sure about the validity or invalidity of iterators because the standard *defines* it, and thus implementors are required to *adhere* to it (and programmers to *check* it). My most profuse apologies for making a three-sentence comment instead of writing up a scientific tract including inline citations. – DevSolar Feb 04 '15 at 15:59
  • @Codepolymer Uly: You might want to read [this SO question](http://stackoverflow.com/questions/6438086) or check [online documentation](http://www.cppreference.com) for the specifics of the various operations on containers. – DevSolar Feb 04 '15 at 16:04
  • @DevSolar: but in the case of `insert` on a `std::vector`, the standard indicates that all references, pointers and iterators are invalidated if reallocation occurs. You can guarantee that reallocation during the insert doesn't occur by calling `reserve` prior to the insert (but the reserve may reallocate, of course, so you'd need to cache the value of `begin` afterwards). (23.3.6.3/6, 23.3.6.5/1) Or am I, too, misunderstanding your point? – rici Feb 04 '15 at 16:15
  • @rici: *sigh*... my point was thus: If a function `X()` of the standard is documented to do A but not B, we can rely on A happening and B not happening because that's what the standard documented, and an implementation not adhering to this would be in fault. And before you all raise *another* ticket on this, yes, there are things that have been *documented* but *not* implemented, `export` being the most cited example, so the usual "exceptions nonwithstanding" applies. So. Have I made myself clear now? ?:-\ – DevSolar Feb 04 '15 at 16:31
  • @DevSolar: More or less, and anyway it's a comment, but I think it would have been clearer to answer "How can we be sure...?" with "Actually, we can't be sure that the address of the beginning of a container remains constant because the standard says it will be invalidated in some circumstances." But whatever. – rici Feb 04 '15 at 16:36

1 Answers1

2

Every container has its own specification as to what operations invalidate which iterators. The specifications are based on the expected implementations.

For example, erase() on a vector will invalidate only iterators past the new end, because all erase() does is destroy objects beyond the new end and update the vector's size: that means that the objects before the new end are not destroyed and they stay at the same memory locations, so iterators and pointers to those objects remain the same.

On the other hand, insert() on a vector will cause the internal buffer to be reallocated if the new size() is larger than the current capacity(), which means all the objects may be moved and thus all the iterators and pointers into the vector would get invalidated, including begin().

bames53
  • 79,748
  • 13
  • 162
  • 229