Last Element in ForEach - c++

Does C++ have a pre-built method to identify which element you are on in your foreach loop, or if there is a way to identify if you are on your last element? Or do I have to do it manually with a counter?

No, there is no such built-in way. Nor could there be for iterators in general, because iterators aren't required to know that they are "almost" at the end.

If you find yourself needing the index your probably better off using a regular for loop

If you need the last element, how about using another iterator to store it before you go to the next item in the container? If you set both to the start and then at the end of you loop before you get the next element (increment or decrement the iterator) , set last iterator to current and then get the next.
Something like this:
container current::iterator;
container last::iterator;
current = container.first();
last = current; // or contianer.first();
while ( current is valid )
{
// do something
last = current;
// get the next item
current = container.next(); // or current++;
// depending upon container or
// iterator
}
That way you don't have to rewrite your loop, but you do need to check to make sure your container is not empty before this.

Related

Using C++ Iterator on a set of one only element

I'm working with iterators on a set of elements of size greater that three almost all the time, but it happens that the generated set contains only one element, in this case, the following loop:
for(i = data_set.begin(); i != data_set.end(); i++)
{
//do something with the data
}
will never be entered even though "data_set" is not empty because data_set.begin()==data_set.end()
I'm doing a test to handle this particular case alone but the code is turning to a mess and is no longer clean.
What should be done to handle this properly?
Thanks,
자스민
If the set contains only 1 element, then:
std::next( data_set.begin() ) == data_set.end(), because begin() iterator points at first element of the container, and end() points to the element that is next after the last one.

C++ loop on map not detecting change of map`s end

I am having a problem while looping thru a map (std::map).
Inside my loop, there is a call to a function which sometimes (not always) erases elements of this same map. After this function is used, there is some code which is using some of this map information as input.
I am having no problems after this function erases any elements, except on the unique case that the last element of the map is erased.
My loop semms not to understand that the last element of the map is not the same as when it started to operate, and will try to operate on elements which doesnt exist, creating a crash.
It seems to me that the myMap.end() call on the loop description is not able to update itself with the new end() of the map.
The relevant part of the code is listed below:
for(std::map<int, ConnectionInfo>::iterator kv = myMap.begin(); kv != myMap.end(); ++kv) {
int thisConnectionID=kv->first; //This is where I get garbage when the loop enters when it shouldnt;
ConnectionInfo currentConnectionInfo=kv->second; //This is where I get garbage when the loop enters when it shouldnt;
status=eraseSomeMapElementsIfNecessary(thisConnectionID,currentConnectionInfo.DownPacket); //this function might erase elements on myMap. This generates no problems afterwards, except when the end element of myMap is erased
... //Next parts of the code make no further usage of myMaps, so I just hid it not to pollute the code
}
Is my interpretation that the kv != myMap.end() is not being able to understand that the inner loop is changing (erasing) the last element (end) of myMap?
In this case, how can I fix this issue?
Or is my interpretation wrong and the solution has nothing to do with what I stated before?
Thanks for your help!
The usual idiom when iterating a map with possibly deleting element is:
for(auto it = map.begin(); it != map.end(); ) {
if ( *it == /*is to delete*/ ) {
it = map.erase(it);
}
else
++it;
}
if your eraseSomeMapElementsIfNecessary might erase some random values in map being iterated then this will for sure cause problems. If element to which it is referencing was erased, it becomes invalid, then incrementing it with ++it is also invalid.
The problem is actually only with the it iterator, if eraseSomeMapElementsIfNecessary erases it and then you use it - you have Undefined Behaviour (UB). So the solution is to pass current iterator to eraseSomeMapElementsIfNecessary, and return from it the next one to iterate:
it = eraseSomeMapElementsIfNecessary(it);
the body of the for loop from my example should be inside your eraseSomeMapElementsIfNecessary function. At least this is one solution.
I am having no problems after this function erases any elements, except on the unique case that the last element of the map is erased.
Erasing an element in any container invalidates the iterator to it. After that you increment the invalidated iterator.
You should increment the iterator before you delete the element pointed by it.
If you do not know what elements that function inside the loop erases assume that all iterators are invalidated.
Maybe these 2 links will help:
How can I delete elements of a std::map with an iterator?
https://stackoverflow.com/a/8234813/3464942
Basically, what it all boils down to, is that you must update the iterator before it becomes invalid.
You have to preserve the next iterator before erasing the current one; since the current one will be invalid after deleting the element.
auto nextit = it+1;
map.erase(it);
it = nextit;

c++ vector object .erase

I have been struggling to put a vector object into a project im doing
I have read what little i could find about doing this and decided to give it a go.
std::vector<BrickFalling> fell;
BrickFalling *f1;
I created the vector. This next piece works fine until i get to the erase
section.
if(brickFall == true){
f1 = new BrickFalling;
f1->getBrickXY(brickfallx,brickfally);
fell.push_back(*f1);
brickFall = false;
}
// Now setup an iterator loop through the vector
vector<BrickFalling>::iterator it;
for( it = fell.begin(); it != fell.end(); ++it ) {
// For each BrickFalling, print out their info
it->printBrickFallingInfo(brick,window,deadBrick);
//This is the part im doing wrong /////
if(deadBrick == true)// if dead brick erase
{
BrickFalling[it].erase;//not sure what im supposed to be doing here
deadBrick = false;
}
}
You can totally avoid the issue by using std::remove_if along with vector::erase.
auto it =
std::remove_if(fell.begin(), fell.end(), [&](BrickFalling& b)
{ bool deadBrick = false;
b.printBrickFallingInfo(brick,window,deadBrick);
return deadBrick; });
fell.erase(it, fell.end());
This avoids the hand-writing of the loop.
In general, you should strive to write erasure loops for sequence containers in this fashion. The reason is that it is very easy to get into the "invalid iterator" scenario when writing the loop yourself, i.e. not remembering to reseat your looping iterator each time an erase is done.
The only issue with your code which I do not know about is the printBrickFallingInfo function. If it throws an exception, you may introduce a bug during the erasure process. In that case, you may want to protect the call with a try/catch block to ensure you don't leave the function block too early.
Edit:
As the comment stated, your print... function could be doing too much work just to determine if a brick is falling. If you really are attempting to print stuff and do even more things that may cause some sort of side-effect, another approach similar in nature would be to use std::stable_partition.
With std::stable_partition you can "put on hold" the erasure and just move the elements to be erased at one position in the container (either at the beginning or at the end) all without invalidating those items. That's the main difference -- with std::stable_partition, all you would be doing is move the items to be processed, but the items after movement are still valid. Not so with std::remove and std::remove_if -- moved items are just invalid and any attempt to use those items as if they are still valid is undefined behavior.
auto it =
std::stable_partition(fell.begin(), fell.end(), [&](BrickFalling& b)
{ bool deadBrick = false;
b.printBrickFallingInfo(brick,window,deadBrick);
return deadBrick; });
// if you need to do something with the moved items besides
// erasing them, you can do so. The moved items start from
// fell.begin() up to the iterator it.
//...
//...
// Now we erase the items since we're done with them
fell.erase(fell.begin(), it);
The difference here is that the items we will eventually erase will lie to the left of the partitioning iterator it, so our erase() call will remove the items starting from the beginning. In addition to that, the items are still perfectly valid entries, so you can work with them in any way you wish before you finally erase them.
The other answer detailing the use of remove_if should be used whenever possible. If, however, your situations does not allow you to write your code using remove_if, which can happen in more complicated situations, you can use the following:
You can use vector::erase with an iterator to remove the element at that spot. The iterator used is then invalidated. erase returns a new iterator that points to the next element, so you can use that iterator to continue.
What you end up with is a loop like:
for( it = fell.begin(); it != fell.end(); /* iterator updated in loop */ )
{
if (shouldDelete)
it = fell.erase(it);
else
++it;
}

offsetting position of iterator for a std::list

Consider the following incomplete snippet:
for (std::list<CollidableNode*>::iterator it = m_Enemies.begin(); it != m_Enemies.end();it++)
{
//for current position+1 to end loop
for (std::list<CollidableNode*>::iterator jt = it+1; jt != m_Enemies.end();jt++)
{
//do stuff
}
}
This code produces obvious errors, but illustrates what I'm trying to do, which is: in the nested loop, set the start point of the loop at the current position in the list, plus one position, so that no duplicate checks are carried out.
Considerations are that the list is highly dynamic in size, with the list being checked for items to remove every update, and new items being added often, so that removals will be faster than a vector.
Is it possible to offset the iterator to a desired position, and if so, how do I go about doing that?
Thanks in advance
Probably std::next is what you need.

Vector.erase(Iterator) causes bad memory access

I am trying to do a Z-Index reordering of videoObjects stored in a vector. The plan is to identify the videoObject which is going to be put on the first position of the vector, erase it and then insert it at the first position. Unfortunately the erase() function always causes bad memory access.
Here is my code:
testApp.h:
vector<videoObject> videoObjects;
vector<videoObject>::iterator itVid;
testApp.cpp:
// Get the videoObject which relates to the user event
for(itVid = videoObjects.begin(); itVid != videoObjects.end(); ++itVid) {
if(videoObjects.at(itVid - videoObjects.begin()).isInside(ofPoint(tcur.getX(), tcur.getY()))) {
videoObjects.erase(itVid);
}
}
This should be so simple but I just don't see where I'm taking the wrong turn.
You should do
itVid = videoObjects.erase(itVid);
Quote from cplusplus.com:
[vector::erase] invalidates all iterator and references to elements after position or first.
Return value: A random access iterator pointing to the new location of the element that followed the last element erased by the function call, which is the vector end if the operation erased the last element in the sequence.
Update: the way you access the current element inside your condition looks rather strange. Also one must avoid incrementing the iterator after erase, as this would skip an element and may cause out-of-bounds errors. Try this:
for(itVid = videoObjects.begin(); itVid != videoObjects.end(); ){
if(itVid->isInside(ofPoint(tcur.getX(), tcur.getY()))){
itVid = videoObjects.erase(itVid);
} else {
++itVid;
}
}
Beware, erasing elements one by one from a vector has quadratic complexity. STL to the rescue!
#include <algorithm>
#include <functional>
videoObjects.erase(
std::remove_if(
std::bind2nd(
std::mem_fun_ref(&videoObject::isInside),
ofPoint(tcur.getX(), tcur.getY())
),
),
videoObjects.end()
);
You cannot delete while iterating over the list because the iterator gets invalid. You should use the return iterator of Erase to set it to your current iterator.
erase function returns the next valid iterator.
You would have to make a while loop and do something like
iterator = erase(...)
with corresponding checks.