deque insert invalidation of iterators vs. pointers ( references) [duplicate] - c++

This question already has an answer here:
C++ deque's iterator invalidated after push_front()
(1 answer)
Closed 8 years ago.
Based on citation from cplusplus.com
If the insertion happens at the beginning or the end of the sequence, all iterators related to this container are invalidated, but pointers and references remain valid, referring to the same elements they were referring to before the call.
Why the insert to the front or end invalidates the iterators but not pointers and references?

Basically, a deque can be thought of as a vector<array<T>*>.
In other words, it consistsof a small "index" vector containing pointers to a series of fixed-size arrays. When you insert at the beginning or end of a deque, it fills out the first/last array, and then adds another one when necessary, so it never needs to move existing elements. That is why pointers/references are not invalidated.
However, because this "index" is stored in something like a vector, it might be copied and reallocated when resized, so every time a new array is added to the index, the index might be copied to a different memory location.
An iterator needs to know enough about the container to be able to iterate over it. In other words, it is not enough to know where the specific element it is current pointing to is located, it also needs to know which array it is part of, and where the index is, so it can find the next/previous arrays.
And so an operation which risks invalidating the "index" also invalidates iterators, because while they might still point to a valid element, they are no longer able to iterate over the entire deque.

Pointers still do have the right memory address of individual items but when you for example:
obtain an iterator pointing to the beginning of the sequence
instert new item to the front
Do you still have an iterator pointing to the beginning? In this example you'll miss the number 5 in output:
#include <deque>
#include <iostream>
using namespace std;
int main()
{
deque<int> d;
d.push_back(1);
d.push_back(2);
d.push_back(3);
deque<int>::iterator it=d.begin();
d.push_front(5);
for(;it!=d.end();++it)
{
cout << *it << endl;
}
}

Related

Iterator to one past the last element in list

I'm working on implementing my own list. I see that std::list::end() returns iterator to one past the last element in the list container. I'm wondering how the position of this past-the-end element is estimated due to list elements are stored in non-contiguous memory locations.
std::list<int> ls;
ls.push_back(1);
ls.push_back(2);
std::list<int>::iterator it = ls.end();
std::cout << &(*it) << std::endl << &(*++it) << std::endl << &(*++it) << std::endl;
As the code above presents, I can even increment the iterator to point to the next elements. How can it be known at which positions (in memory) the next elements will be stored?
How can it be known at which positions (in memory) the next elements will be stored?
It is not. Also, using that memory address as (part of) the past-the-end iterator would be incorrect.
Is not:
An iterator is not (necessarily) a pointer. An iterator is not required to store a memory address. What is required is that the de-reference operator be able to calculate a memory address (returned in the form of a reference). Good news, everyone! Applying the de-reference operator to the past-the-end iterator is undefined behavior. So even this reduced requirement is not applicable to the past-the-end iterator. If you are storing an address, go ahead and store whatever you want. (Just be consistent since two past-the-end iterators must compare equal.)
If your iterator does store a pointer (which admittedly is probably common), a simple approach would be to store whatever you would put in the next field of the last node in the list. This is typically either nullptr or a pointer to the list's sentinel node.
Would be incorrect:
A std::list does not invalidate iterators when elements are added to the list. This includes the past-the-end iterator. (See cppreference.com.) If your past-the-end iterator pointed to where the next element would be stored, it would be invalidated by adding that element to the list. Thus, you would fail to meet the iterator invalidation requirements for a std::list. So not only is storing that address in the past-the-end iterator impossible, it's not allowed.

How to dynamically update the condition of a for loop in C++?

Take the following code snippet:
// x is a global vector object that holds values of type string as follows, vector<string> x
// x is filled/populated via the function Populate_x(y,z);
Populate_x(y,z);
for (auto i : x)
{
string v = check(i);
Populate_x(v,v);
}
My question is, how can I dynamically update x in the range based for loop shown above when calling Populate_x(v,v) from within the for loop? I'm not sure if this is even possible. If not, how can I restructure my code to achieve this behavior?
Your suggestions are greatly appreciated.
A range-based for loop is equivalent to, approximately: 1) get the container's beginning and ending iterator, 2) Use a temporary iterator to iterate from the beginning iterator value to the ending iterator value 3) On each iteration, dereference the temporary iterator and set the range loop's variable to the dereferenced iterator value.
... more or less. The bottom line is 1) a range-based for loop obtains and uses the beginning and the ending iterators for the range, and 2) you state that your container is a vector, and, as you know, modifying a vector is going to invalidate most iterators to the contents of the vector.
It is true that with certain kinds of modifications to the contents of the vector, certain iterators will not be invalidated, and will remain valid. But, practically, it is safe to assume that if you've got a std::vector::iterator or a std::vector::const_iterator somewhere, modifying a vector means that the iterator is no longer valid. Not always 100% true, as I mentioned, but that's a pretty safe assumption to make.
And since range iteration obtains and uses iterators to the container, for the lifetime of the iteration, that makes, pretty much, doing a range iteration over a vector, and modifying the vector during iteration, a non-starter. Any modifications to the vector will likely result in undefined behavior, for any continuing iteration over the vector.
Note that "modification" means, essentially, insertion or removal of values from the vector; that is, modification of the vector itself. Modifying the values in the vector has no effect on the validity of any existing iterators, and this is safe.
If you want to iterate over a vector, and then safely modify the vector during this process (with the modification consisting of inserting or removing value from the vector), the first question you have to answer yourself is what does the insertion or removal mean for your iteration. That's something that you have to figure out yourself. If, for example, your loop is currently on the 4th element in the vector, and you insert a new 2nd value in the vector, since inserting a value into the vector shifts all the remaining values in the vector up, the 4th element in the vector will become the 5th element in the vector, and if you manage to safely do this correctly, on the next iteration, the 5th element in the vector will be the same one you just iterated over previously. Is this what you want? That's something you will have to answer yourself, first.
But as far as safely modifying a vector during iteration, the most simplest way is to avoid using iterators entirely, and use an index variable:
for (size_t i=0; i<x.size(); ++i)
{
auto v=x[i];
// ...
}
Now, modifying the vector is perfectly safe, and all you have to figure out, then, is what has to happen after the vector gets modified, whether the i index variable needs adjusting, in any way.
And that question you'll have to answer yourself.
From your description, I'm not sure what you mean by dynamically update x.
Is Populate_x adding new elements to x ?
Assuming your Populate_x function tries to push_back some elements on your vector x, then you cannot do that. See this answer for more details
Modifying the vector inside the loop results in undefined behaviour because the iterators used by the loop are invalidated when the vector is modified.
If so, if you want to add multiple elements at the end of x, a practical way would be to use a temporary vector<string> y; , push_back/emplace_back elements into y, and then once you're ready to append all elements of y into x , do something similar to this (in this case v1 is your x, v2 is your y :
x.insert(x.end(), make_move_iterator(y.begin()), make_move_iterator(y.end()));

How to erase element from vector and update iterator?

I am trying to delete a object from a vector at a specific index. The vector iterator keeps track of the index throughout the program. In the below code, the first IF statement works perfectly. But, if the iterator is pointing to anywhere OTHER than the last element, I erase the element from the vector and then increment the iterator. The program crashes and says "iterator not incrementable".
I ran the debugger several times and everything looks correct, so I cannot see what I am missing?
vector<Card> myVector; //container to hold collection of cards.
vector<Card>::iterator myVectorIterator; //points to each "card" in the collection.
Card Collection::remove()
{
if (myVectorIterator== myVector.end()-1) { //at the last card
//erase the "current" card
myVector.erase(myVectorIterator);
//update to the first card.
myVectorIterator= myVector.begin();
}
else
{
myVector.erase(myVectorIterator);
//crashes here!
myVectorIterator++;
}
return *myVectorIterator;
}
erase invalidates the iterator, so you can't use it afterwards. But it helpfully returns an iterator to the element after the removed one:
myVectorIterator = myVector.erase(myVectorIterator);
This is because the call to erase invalidates all iterators. Imagine you have something pointing to an element and that element disappears, what shall you point to?
Worse still, the behavior is highly implementation dependent. The correct approach is to store use the return value from erase which will be an iterator to the next element in the vector.
myVectorIterator = myVector.erase(myVectorIterator);
Note that you now have to remove the incrementation on the next line (or you will skip an item.)
In general, when manipulating different STL containers, you will see that in the documentation whether a given operation will have an effect on iterators. If you take a look at this link, you will see that for vector::erase this is the case:
"Iterators, pointers and references pointing to position (or first) and beyond are invalidated, with all iterators, pointers and references to elements before position (or first) are guaranteed to keep referring to the same elements they were referring to before the call."
Different containers may have different guarantees when it comes to iterator validity.
You have to do this
myVectorIterator = myVector.erase(myVectorIterator);
It will remove the current iterator then assign the next item into it.

Are vector elements guaranteed to be in order?

I understand that having pointers to elements of a vector is a bad idea, because upon expanding, the memory addresses involved will change, therefore invalidating the pointer(s). However, what if I were to simply use an integer that holds the index number of the element I want to access? Would that be invalidated as the vector grows in size? What I am thinking of looks something like this:
#include <vector>
class someClass{
string name
public: string getName(){return name;}
};
vector<someClass> vObj;
int currIdx;
string search;
cout<<"Enter name: ";
cin>>search;
for(int i=0; i<vObj.size(); i++){
if(vObj[i].getName()==search)
currIdx = i;}
No, the index numbers are of course not invalidated when the vector expands. They are invalidated (in the sense that you no longer find the same elements at a constant index) if you erase a previous element, though:
vector: 3 5 1 6 7 4
Here, vector[2] == 1. But if you erase vector[1] (the 5), then afterwards, vector[2] == 6.
I think the title of your question and what you seem to be asking do not really match. No vector is by definition guaranteed to be sorted, so elements won't be "in order".
Moreover, all iterators and references to elements of a vector will be invalidated upon insertion only if reallocation occurs (i.e. when the size of the vector exceeds its capacity). Otherwise, iterators and references before the point of insertion will not be invalidated (see Paragraph 23.3.6.5/1 of the C++11 Standard).
Storing an index is only subject to a potential logical invalidation: if you insert elements into the vector in a position prior to the one you are indexing, the element you were indexing will be shifted one position to the right, and the same index will now refer to a different element ; likewise, if you erase an element prior to the position you were indexing, the element you were indexing will be shifted on position to the left - and your index may now refer to a position which is out-of-bounds.
No, the index numbers are not invalidated when the vector is expanded. Since you're declaring that the vector container object is not a pointer vector<someClass> instead of vector<someClass*>, your pointed to element will be preserved as well.
It shouldn't, as the system will simply allocate more memory and then do a memcopy.
Order should be preserved in the std::vector STL template.
And yes, if you delete elements the ordering will change. But if you are going to be doing a lot of deletes, use a different data structure such as a linked list.

Wrong results when appending vector to itself using copy and back_inserter [duplicate]

This question already has answers here:
Nice way to append a vector to itself
(4 answers)
Closed 8 years ago.
Inspired by this question, asking how to append a vector to itself, my first thought was the following (and yes, I realize insert is a better option now):
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::vector<int> vec {1, 2, 3};
std::copy (std::begin (vec), std::end (vec), std::back_inserter (vec));
for (const auto &v : vec)
std::cout << v << ' ';
}
However, this prints:
1 2 3 1 * 3
The * is a different number every time the program is run. The fact that it's only the 2 being replaced is peculiar, and if there actually is an explanation for that, I'd be interested to hear it. Continuing, if I append to a different vector (a copy of the original), it outputs correctly. It also outputs correctly if I add the following line before the copy one:
vec.reserve (2 * vec.size());
I was under the impression std::back_inserter was a safe way to add elements onto the end of a container, despite not reserving memory beforehand. If my understanding is correct, what's wrong with the copying line?
I assume it's nothing to do with the compiler, but I'm using GCC 4.7.1.
std::back_inserter creates an inserting iterator that inserts elements into a container. Each time this iterator is dereferenced, it calls push_back on the container to append a new element to the container.
For a std::vector container, a call to push_back where v.size() == v.capacity() will result in a reallocation: a new array is created to store the contents of the vector, its current contents are copied into the new array, and the old array is destroyed. Any iterators into the vector at this time are invalidated, meaning they can no longer be used.
In your program, this includes the input range defined by begin(vec) and end(vec) from which the copy algorithm is copying. The algorithm continues to use these iterators, even though they are invalidated, thus your program exhibits undefined behavior.
Even if your container had sufficient capacity, its behavior would still be undefined: the specification states that, upon insertion, "if no reallocation happens, all the iterators and references before the insertion point remain valid" (C++11 ยง23.3.6.5/1).
The call to push_back is equivalent to insertion at the end, so the end iterator (std::end(vec)) that you passed into std::copy is invalidated after a single call to push_back. If the input range is nonempty, the program therefore exhibits undefined behavior.
Note that the behavior of your program would be well-defined if you used a std::deque<int> or a std::list<int>, because neither of those containers invalidates iterators when elements are appended.