Why is iterator of container invalidated when this container is also an element of a container? - c++

int main() {
std::vector<std::vector<int>> v;
v.push_back({1,2,3,4});
auto it = v.at(0).begin();
int size = v.at(0).size();
std::cout<<size<<std::endl;
for (int i = 0; i < size; ++it)
{
v.push_back({5,6,7,8});
//std::cout<<*it<<std::endl;
}
return 0;
}
The iterator is broken when I push some elements into the outer container.
what should I do if I really want to iterate the container element inside of outer container and at the same time keep pushing back some new elements ? Many thanks!

When the outer vector resizes, it must do one of two things:
copy the elements
move the elements
It can only move the elements if the type has nonthrowing move semantics (the move constructor is marked noexcept, etc).
In this case, the element is a vector holding integers, which recursively depends on its elements. Since integer does not throw, the inner vector should be noexcept-movable too. The standard requires the iterators to remain valid when moving such a vector.
You say iterator is broken in your example, but the real problem is a bug in your code:
for (int i = 0; i < size; ++it) // << HERE
{
v.push_back({5,6,7,8});
//std::cout<<*it<<std::endl;
}
You don't increment the loop variable, you increment the iterator, in an infinite loop

what should I do if I really want to iterate the container element inside of outer container and at the same time keep pushing back some new elements ?
I see 3 possible solutions:
use container that does not relocate elements when new ones are added, for example std::map<size_t,std::vector<int>>, you should be aware of different memory usage and access speed
use index of inner array instead of iterator
add data to a temporary vector in the loop and after you done append all data to the original vector

Related

C++ Keeping track of start iterator while adding items

I am trying to do a double loop across a std::vector to explore all combinations of items in the vector. If the result is good, I add it to the vector for another pass. This is being used for an association rule problem but I made a smaller demonstration for this question. It seems as though when I push_back it will sometimes change the vector such that the original iterator no longer works. For example:
std::vector<int> nums{1,2,3,4,5};
auto nextStart = nums.begin();
while (nextStart != nums.end()){
auto currentStart = nextStart;
auto currentEnd = nums.end();
nextStart = currentEnd;
for (auto a = currentStart; a!= currentEnd-1; a++){
for (auto b = currentStart+1; b != currentEnd; b++){
auto sum = (*a) + (*b);
if (sum < 10) nums.push_back(sum);
}
}
}
On some iterations, currentStart points to a location that is outside the array and provides garbage data. What is causing this and what is the best way to avoid this situation? I know that modifying something you iterate over is an invitation for trouble...
nums.push_back(sum);
push_back invalidates all existing iterators to the vector if push_back ends up reallocating the vector.
That's just how the vector works. Initially some additional space gets reserved for the vector's growth. Vector's internal buffer that holds its contents has some extra room to spare, but when it is full the next call to push_back allocates a bigger buffer to the vector's contents, moves the contents of the existing buffer, then deletes the existing buffer.
The shown code creates and uses iterators for the vector, but any call to push_back will invalidate the whole lot, and the next invalidated vector dereference results in undefined behavior.
You have two basic options:
Replace the vector with some other container that does not invalidate its existing iterators, when additional values get added to the iterator
Reimplement the entire logic using vector indexes instead of iterators.

C++: how to loop through integer elements in a vector

I would like to loop through elements of a vector in C++.
I am very new at this so I don't understand the details very well.
For example:
for (elements in vector) {
if () {
check something
else {
//else add another element to the vector
vectorname.push_back(n)
}
}
Its the for (vector elements) that I am having trouble with.
You'd normally use what's called a range-based for loop for this:
for (auto element : your_vector)
if (condition(element))
// whatever
else
your_vector.push_back(something);
But note: modifying a vector in the middle of iteration is generally a poor idea. And if your basic notion is to add the element if it's not already present, you may want to look up std::set, std::map, std::unordered_set or std::unordered_map instead.
In order to do this properly (and safely), you need to understand how std::vector works.
vector capatity
You may know that a vector works much like an array with "infinite" size. Meaning, it can hold as many elements as you want, as long as you have enough memory to hold them. But how does it do that?
A vector has an internal buffer (think of it like an array allocated with new) that may be the same size as the elements you're storing, but generally it's larger. It uses the extra space in the buffer to insert any new elements that you want to insert when you use push_back().
The amount of elements the vector has is known as its size, and the amount of elements it can hold is known as its capacity. You can query those via the size() and capacity() member functions.
However, this extra space must end at some point. That's when the magic happens: When the vector notices it doesn't have enough memory to hold more elements, it allocates a new buffer, larger1 than the previous one, and copies all elements to it. The important thing to notice here is that the new buffer will have a different address. As we continue with this explanation, keep this in mind.
iterators
Now, we need to talk about iterators. I don't know how much of C++ you have studied yet, but think of an old plain array:
int my_array[5] = {1,2,3,4,5};
you can take the address of the first element by doing:
int* begin = my_array;
and you can take the address of the end of the array (more specifically, one past the last element) by doing:
int* end = begin + sizeof(my_array)/sizeof(int);
if you have these addresses, one way to iterate the array and print all elements would be:
for (int* it = begin; it < end; ++it) {
std::cout << *it;
}
An iterator works much like a pointer. If you increment it (like we do with the pointer using ++it above), it will point to the next element. If you dereference it (again, like we do with the pointer using *it above), it will return the element it is pointing to.
std::vector provides us with two member functions, begin() and end(), that return iterators analogous to our begin and end pointers above. This is what you need to keep in mind from this section: Internally, these iterators have pointers that point to the elements in the vector's internal buffer.
a simpler way to iterate
Theoretically, you can use std::vector::begin() and std::vector::end to iterate a vector like this:
std::vector<int> v{1,2,3,4,5};
for (std::vector<int>::iterator it = v.begin; it != v.end(); ++it) {
std::cout << *it;
}
Note that, apart from the ugly type of it, this is exactly the same as our pointer example. C++ introduced the keyword auto, that lets us get rid of these ugly types, when we don't really need to know them:
std::vector<int> v{1,2,3,4,5};
for (auto it = v.begin; it != v.end(); ++it) {
std::cout << *it;
}
This works exactly the same (in fact, it has the exact same type), but now we don't need to type (or read) that uglyness.
But, there's an even better way. C++ has also introduced range-based for:
std::vector<int> v{1,2,3,4,5};
for (auto it : v) {
std::cout << it;
}
the range-based for construct does several things for you:
It calls v.begin() and v.end()2 to get the upper and lower bounds of the range we're going to iterate;
Keeps an internal iterator (let's call it i), and calls ++i on every step of the loop;
Dereferences the iterator (by calling *i) and stores it in the it variable for us. This means we do not need to dereference it ourselves (note how the std::cout << it line looks different from the other examples)
putting it all together
Let's do a small exercise. We're going to iterate a vector of numbers, and, for each odd number, we are going to insert a new elements equal to 2*n.
This is the naive way that we could probably think at first:
std::vector<int> v{1,2,3,4,5};
for (int i : v) {
if (i%2==1) {
v.push_back(i*2);
}
}
Of course, this is wrong! Vector v will start with a capacity of 5. This means that, when we try using push_back for the first time, it will allocate a new buffer.
If the buffer was reallocated, its address has changed. Then, what happens to the internal pointer that the range-based for is using to iterate the vector? It no longer points to the buffer!
This it what we call a reference invalidation. Look at the reference for std::vector::push_back. At the very beginning, it says:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
Once the range-based for tries to increment and dereference the now invalid pointer, bad things will happen.
There are several ways to avoid this. For instance, in this particular algorithm, I know that we can never insert more than n new elements. This means that the size of the vector can never go past 2n after the loop has ended. With this knowledge in hand, I can increase the vector's capacity beforehand:
std::vector<int> v{1,2,3,4,5};
v.reserve(v.size()*2); // Increases the capacity of the vector to at least size*2.
// The code bellow now works properly!
for (int i : v) {
if (i%2==1) {
v.push_back(i*2);
}
}
If for some reason I don't know this information for a particular algorithm, I can use a separate vector to store the new elements, and then add them to our vector at the end:
std::vector<int> v{1,2,3,4,5};
std::vector<int> doubles;
for (int i : v) {
if (i%2==1) {
doubles.push_back(i*2);
}
}
// Reserving space is not necessary because the vector will allocate
// memory if it needs to anyway, but this does makes things faster
v.reserve(v.size() + doubles.size());
// There's a standard algorithm (std::copy), that, when used in conjunction with
// std::back_inserter, does this for us, but I find that the code bellow is more
// readable.
for (int i : doubles) {
v.push_back(i);
}
Finally, there's the old plain for, using an int to iterate. The iterator cannot be invalidated because it holds an index, instead of a pointer to the internal buffer:
std::vector<int> v{1,2,3,4,5};
for (int i = 0; i < v.size(); ++i) {
if (v[i]%2==1) {
doubles.push_back(v[i]*2);
}
}
Hopefully by now, you understand the advantages and drawbacks of each method. Happy studies!
1 How much larger depends on the implementation. Generally, implementations choose to allocate a new buffer of twice the size of the current buffer.
2 This is a small lie. The whole story is a bit more complicated: It actually tries to call begin(v) and end(v). Because vector is in the std namespace, it ends up calling std::begin and std::end, which, in turn, call v.begin() and v.end(). All of this machinery is there to ensure that the range-based for works not only with standard containers, but also with anything with a proper implementation for begin and end. That includes, for instance, regular plain arrays.
Here is the quick code snippet using iterators to iterate the vector-
#include<iostream>
#include<iterator> // for iterators to include
#include<vector> // for vectors to include
using namespace std;
int main()
{
vector<int> ar = { 1, 2, 3, 4, 5 };
// Declaring iterator to a vector
vector<int>::iterator ptr;
// Displaying vector elements using begin() and end()
cout << "The vector elements are : ";
for (ptr = ar.begin(); ptr < ar.end(); ptr++)
cout << *ptr << " ";
return 0;
}
Article to read more - Iterate through a C++ Vector using a 'for' loop
.
Hope it will help.
Try this,
#include<iostream>
#include<vector>
int main()
{
std::vector<int> vec(5);
for(int i=0;i<10;i++)
{
if(i<vec.size())
vec[i]=i;
else
vec.push_back(i);
}
for(int i=0;i<vec.size();i++)
std::cout<<vec[i];
return 0;
}
Output:
0123456789
Process returned 0 (0x0) execution time : 0.328 s
Press any key to continue.

c++ vector insertion and reading

I am inserting elements with push_back in a vector. I want to read the data in FIFO and using iterator assigned to begin of vector. Is there any other approach of reading data in FIFO in a vector?
You may use a std::deque() and its pop_front() method.
You can access the elements of a vecotr just like you would access the elements of an array:
std::vector<std::string> vec;
// Excluded: push items onto vec
for (int i = 0; i < vec.size(); ++i) {
// Example:
std::cout << vec[i];
}
The code would be:
auto value = myvector[0];
myvector.erase(myvector.begin());
However, removing elements from the beginning (or somewhere in between) is slow, because it must copy the whole array. Access is fast though: vector allows random access (i.e. access by any explicit index) in O(1) (i.e. constant access time, i.e. very fast).
But another container structure instead of vector might make more sense for you, e.g. list or deque. Some STL implementations (or other framework) also have something like rope which is in many cases the best of both worlds.
There's nothing special to pay attention to. To insert, use
push_back, to extract, you need something like:
if ( !fifo.empty() ) {
ValueType results = fifo.front();
fifo.erase( fifo.begin() );
}
(Don't forget to check for empty before trying to remove an
element.)
An important point to remember is that both push_back and
in some cases erase can invalidate iterators, so you don't
want to keep iterators into the underlying vector hanging
around.

Erasing elements in a vector

So I have a vector of unsigned ints (vector<unsigned int> is called vector1). I have another vector of a struct I created (vector<struct> is called vector2). vector<int> holds an integer that is the index of the vector<struct>. For example, let's say that vector<int = {5, 17, 18, 19}. That means vector2.at(5) == vector2.at(vector1.at(0)).
In the struct, I have a bool variable called var. In most cases, var is false. I want to delete all of the elements in vector1 that have var = true.
What I did was:
for (unsigned int i = 0; i < vector1.size(); i++)
{
if (vector2.at(vector1.at(i)).var)
vector1.erase(vector.begin() + i);
}
The only problem with this is that it does not delete all of the true elements. I have run the for loop multiple times for all values to be delete. Is this the correct behavior? If it is not, where did I go wrong?
You have to use the erase-remove idiom to delete elements from a vector.
v.erase(std::remove(v.begin(), v.end(), value), v.begin);
std::remove moves the elements to the end of the vector and erase will erase the element from the vector.
You can keep a temporary vector, copy of vector1 and iterate over it in the for loop and delete from vector1.
You are erasing elements in the vector while at the same time iterating over it. So when erasing an element, you always jump over the next element, since you increase i while having just shortened the vector at i (it would be even worse, had you used a proper iterator loop instead of an index loop). The best way to do this would be to seperate both opretions, first "marking" (or rather reordering) the elements for removal and then erasing them from the vector.
This is in practice best done using the erase-remove idiom (vector.erarse(std::remove(...), vector.end())), which first uses std::remove(_if) to reorganize the data with the non-removed elements at the beginning and returns the new end of the range, which can then be used to really delete those removed elements from the range (effectively just shortening the whole vector), using std::vector::erase. Using a C++11 lambda, the removal condition can be expressed quite easily:
vector1.erase(std::remove_if( //erase range starting here
vector1.begin(), vector1.end(), //iterate over whole vector
[&vector2](unsigned int i) //call this for each element
{ return vector2.at(i).var; }), //return true to remove
vector1.end()); //erase up to old end
EDIT: And by the way, as always be sure if you really need std::vector::at instead of just [] and keep in mind the implications of both (in particular the overhead of the former and "maybe insecurity" of the latter).

C++ reorder std::vector elements using std::list of pointers

I ran into this problem when I tried to write out an new algorithm to reorder elements in std::vector. The basic idea is that I have std::list of pointters pointing into std::vector in such way that *list.begin() == vector[0], *(++list.begin()) == vector[1] and so on.
However, any modifications on list's element positions breaks the mapping. (Including appended pointers) When the mapping is broken the list's elements can be in random order but they point still into correct elements on vector. The task would be to reorder the elements in vector to correct the mapping.
Simplest method to do it (How I have done it now):
create new empty std::vector and resize it to equal size of the old vector.
iterate through the list and read elements from the old vector and write them into new vector. Set the pointer to point into new vector's element.
swap vectors and release the old vector.
Sadly the method is only useful when I need more capacity on the vector. It's inefficient when the current vector holding the elements has enough capacity to store all incoming elements. Appended pointers on the list will point into diffrent vector's storgate. The simple method works for this because it only reads from the pointers.
So I would want to reorder the vector "in place" using constant amount of memory. Any pointer that was not pointing into current vector's storgate are moved to point into current vector's storgate. Elements are simple structures. (PODs)
I'll try post an example code when I have time..
What should I do to achieve this? I have the basic idea done, but I'm not sure if it is even possible to do the reordering with constant amount of memory.
PS: I'm sorry for the (possibly) bad grammar and typos in the post. I hope it's still readable. :)
First off, why do you have a list of pointers? You might as well keep indices into the vector, which you can compute as std::distance(&v[0], *list_iter). So, let's build a vector of indices first, but you can easily adapt that to use your list directly:
std::vector<T> v; // your data
std::list<T*> perm_list; // your given permutation list
std::vector<size_t> perms;
perms.reserve(v.size());
for (std::list<T*>::const_iterator it = perm_list.begin(), end = perm_list.end(); it != end; ++it)
{
perms.push_back(std::distance(&v[0], *it));
}
(There's probably a way to use std::transform and std::bind, or lambdas, to do this in one line.)
Now to do the work. We simply use the cycle-decomposition of the permutation, and we modify the perms vector as we go along:
std::set<size_t> done;
for (size_t i = 0; i < perms.size(); while(done.count(++i)) {})
{
T tmp1 = v[i];
for (size_t j = perms[i]; j != i; j = perms[j])
{
T tmp2 = v[j];
v[j] = tmp1;
tmp1 = tmp2;
done.insert(j);
}
v[i] = tmp1;
}
I'm using the auxiliary set done to track which indices have already been permuted. In C++0x you would add std::move everywhere to make this work with movable containers.