I'm trying to add a value to a vector after every 5 elements. This example gives me an assert error
Expression: cannot seek vector iterator after end
The error is self explanatory however the code executes in an online compiler and gives the desired result. I can just do 1 less iteration in which case the final insert won't happen and return an error. I also know creating a struct would be a better way to tackle this, but I'd like to know how/why I'm struggling with this iterator method.
The vertexSize divides evenly into targetBuffer in every case so that isn't an issue.
//error example
int vertexSize = 5;
int size = targetBuffer.size() / vertexSize;
std::cout << targetBuffer.size() << std::endl;
std::cout << vertexSize << std::endl;
std::vector<float>::iterator it;
it = targetBuffer.begin() vertexSize;
std::cout << std::distance(targetBuffer.begin(), it) << std::endl;
for (int i = 0; i < size; i ) {
it = targetBuffer.insert(it, (unsigned int)(i * vertexSize) / (24 * vertexSize));
it = vertexSize 1;
}
However if I run this below example in an online compiler and it runs and gives the desired result.
int vertexSize = 3;
std::vector<int> targetBuffer;
for(int i = 0; i < 1002; i ){
targetBuffer.push_back(-1);
}
int size = targetBuffer.size() / 3;
for (int i = 0; i < 110; i ) {
std::cout << i <<":" << " " << targetBuffer[i] << std::endl;
}
std::vector<int>::iterator it;
it = targetBuffer.begin() vertexSize;
for (int i = 0; i < size; i ) {
//(unsigned int)(i*5)/(24*5)
it = targetBuffer.insert(it, (unsigned int)(i * vertexSize) / (24 * vertexSize));
std::advance(it, 3);
}
std::cout << "NEW SIZE " << targetBuffer.size() << std::endl;
for (int i = 0; i < 110; i ) {
std::cout << i <<":" << " " << targetBuffer[i] << std::endl;
}
for (int i = 900; i < targetBuffer.size(); i ) {
std::cout << i <<":" << " " << targetBuffer[i] << std::endl;
}
Original vector
0: -1
1: -1
2: -1
3: -1
4: -1
5: -1
6: -1
7: -1
8: -1
intial iterator
it = targetBuffer.begin() vertexSize;
Inserting attribute
it = targetBuffer.insert(it, (unsigned int)(i * vertexSize) / (24 * vertexSize));
it = vertexSize 1;
0: -1
1: -1
2: -1
3: 0
4: -1
5: -1
6: -1
7: 0
8: -1
9: -1
10: -1
11: 0
So I have a few observations about using iterators. If I disinclude the 1 from the addition it then inserts only 2 places instead of 3 as seen below. But the first inserts after 3 spaces.
it = targetBuffer.insert(it, (unsigned int)(i * vertexSize) / (24 * vertexSize));
it = vertexSize;
0: -1
1: -1
2: -1
3: 0
4: -1
5: -1
6: 0
7: -1
8: -1
9: 0
My guess is that this is due to the resizing of the vector and the iterator not accounting for the newly added value or something along those lines? Maybe I should reevaluate the iterator in each iteration of the loop i.e.
for(...)
it = targetBuffer.begin() i * vertexSize;
or something along these lines
CodePudding user response:
The issue is simply that it is forbidden to increment an iterator beyond the end iterator even if you never try to dereference the result.
When you write std::advance(it, 3);
or it = vertexSize 1;
and the size of the original vector is not divisible by the step size you chose, the resulting value of it
may be beyond the end of the vector in the last loop iteration. Because it is the last loop iteration you don't actually reenter the loop body and use that value of it
, but it is simply not allowed to form this iterator in the first place and doing so causes undefined behavior. That it happens to work sometimes doesn't change that. (That's the problem with undefined behavior. It may appear to work sometimes but there is absolutely no guarantee about how it will behave.)
You can resolve this by rewriting your loop so that you increment the iterator in the loop first before inserting, so that the problematic increment you currently have in the last loop iteration is dropped:
it = targetBuffer.begin();
for (int i = 0; i < size; i ) {
std::advance(it, vertexSize);
it = targetBuffer.insert(it, (unsigned int)(i * vertexSize) / (24 * vertexSize));
// `insert` inserts _before_ the passed iterator,
// so get back to that element
std::advance(it, 1);
}
However, this algorithm has terrible performance. Its time complexity is quadratic in the length of the vector. It would be better to create a new vector, call .reserve
(or .resize
) on it to reserve the correct size, then loop through the vector and .push_back
copies of the old and the new elements (or index into it instead of using .push_back
). Finally move-assign or swap the new vector with the old one.