0

If vectors are stored contiguously, as long as they are not made smaller or reallocated, any iterator pointing to an element within it should be valid. The following code would be defined:

#include <iostream>
#include <vector>

int main()
{
    std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 
    auto i = v.begin() + 7;
    v.insert(v.begin() + 5, 10);
    if(v.capacity() > v.size()) {
        std::cout << *i << '\n';
    }
}

This writes 7 to my console, which my logic would predict. However, it is wrong according to GeeksforGeeks:

All iterators which point to an element before the point of insertion are unaffected but all others are invalidated.

Why is it wrong? I ask because I cannot see how the code would fail.

19
  • 12
    Side note: That Geeks For Geeks link looses a ton of credibility at the first #include <bits/stdc++.h>. Tread cautiously on that site. Lot of BS floating around on Geeks For Geeks. Commented Jul 31 at 21:46
  • 7
    v.capacity() > v.size() after the fact is not a good check for "vector has not been re-allocated". It is very likely that v.capacity() > v.size() immediately after a reallocation. Commented Jul 31 at 21:48
  • 2
    Unless you can prove that the insert can never trigger a reallocation and will always be after outstanding iterators, assume that any insert will invalidate iterators. If you simply assume that it can't resize, sooner or later you'll have to fix a bug. Commented Jul 31 at 22:12
  • 5
    Your if(v.capacity() > v.size()) { offers no protection, and actually offers a false sense of security. For any vector (assuming no undefined behaviour has occurred) v.capacity() >= v.size() will always be true. Your test only checks if v.capacity() > v.size() is true AFTER doing the insert operation. Whereas, the insert operation avoids resizing ONLY if the original v.capacity() (BEFORE inserting) is not less than the new size (AFTER inserting). Commented Jul 31 at 23:17
  • 3
    Tip: Never store or reuse iterators after adding/removing content from a container unless it is explicitly documented it is safe to do so. Note that container operation usually warn you. E.g read std::vector::push_back and you will find out. (Now unlike geekforgeeks, cppreference is a site you should listen to) Commented Aug 1 at 3:27

3 Answers 3

6

A better reference to look at is https://en.cppreference.com/w/cpp/container/vector/insert which states:

If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise, only the iterators and references before the insertion point remain valid.

So the proper check is:

#include <iostream>
#include <vector>

int main()
{
    std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 
    auto i = v.begin() + 7;
    size_t oldCapacity = v.capacity();
    v.insert(v.begin() + 5, 10);
    if(v.size() <= oldCapacity) {
        std::cout << *i << '\n';
    }
}

Although this code will generally work, i has still been invalidated, before the insert it pointed to 8, after the insert it points to 7 this means that the iterator has been invalidated, a valid iterator always points to the same item in a container. Dereferencing an invalidated iterator is undefined behaviour.

Sign up to request clarification or add additional context in comments.

1 Comment

@JaMiT it's linked to by the standards committee isocpp.org/std/the-standard and I believe is primarily maintained by committee members too
1

What you observe is the details of specific implementation you are dealing with (i.e. this is how your application works on specific platform built by specific version of the compiler). However C++ is the language which is implemented based on so-called standard. This document describes contract of what the implementation must maintain for every version of the language. You didn't specify which language version you use, but if you refer to draft n4861 (C++ 20), you can find that it requires any iterator which points at of after position of the inserted element to be invalidated (vector.modifiers/1, emphasis mine):

Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator. If no reallocation happens, then references, pointers, and iterators before the insertion point remain valid but those at or after the insertion point, including the past-the-end iterator, are invalidated.

This is self-explanatory on its own, but the standard even defines what it means to dereference such an iterator in the footnote of iterator.requirements.general:

...The effect of dereferencing an iterator that has been invalidated is undefined.

The "undefined" is not an abstract term in the standard. It essentially means, that the compiler vendors are free to implement whatever they want in this scenario, even if it's counter-intuitive to the programmer (this freedom, however, helps to introduce optimization to the program). So if you have a program with undefined behavior, it means you cannot reliably know how it will work (and whether it will work at all) in any other versions of the compiler (even from the same vendor) or other platforms.

Comments

1

There is a note at the bottom of the page to which you linked.

Note: Invalidation of iterator does not always mean that dereferencing such an iterator causes a program to crash. It also includes the possibility that iterator does not point to an element which it is supposed to point.

When your code initializes i, it points to the element whose value is 8. Since your code does not change i, it is supposed to point to the element whose value is 8. After inserting, though, you found that it points to the element whose value is 7. Therefore, it does not point to the element to which it is supposed to point. I.e., your test run proves that the iterator was invalidated.

(The above quote understates the case. In fact, anything is possible when you de-reference an invalid iterator, including getting 8 as the output.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.