We're currently using Coverity's Synapsis which runs over the code base and flags lines of code that would cause a bug and such.
I have this code:
auto it = std::find_if(my_container.my_list.begin(), my_container.my_list.end(),
[&](my_struct temp)
{
return temp._id == id;
});
/*To erase duplicates*/
if (it != my_container.my_list.end())
{
my_container.my_list.erase(it);
}
The erase part is being recognized as "erase invalidates iterator " then "Using invalid iterator (INVALIDATE_ITERATOR)". I'm not sure I understand why this is so. The iterator is not used after this code so it should be safe, right?
Related
I have a multimap which key is short and value is the other multimap
std::multimap<short,std::multimap<short,short>> multimap
now I want to do it`
std::multimap<short,short> &a=multimap.find(0)->second;
std::pair<short,short> z={1,2};
a.insert(z);
It compiles fine. But when I run it it just stops and doesn't finish the process, It even doesn't throw any runtimeerror. Have any ideas? Thanks in advice.
Having
std::multimap<short,std::multimap<short,short>> multimap
...
std::multimap<short,short> &a=multimap.find(0)->second;
std::pair<short,short> z={1,2};
a.insert(z);
If find returns multimap::end that one shall not be dereferenced, but you do and get a reference to second, the behavior is undefined when later to use that reference to insert.
So of course check if find succes, like
std::multimap<short,std::multimap<short,short>> multimap;
std::multimap<short,std::multimap<short,short>>::iterator it = multimap.find(0);
if (it == multimap.end()) {
...
}
else {
std::multimap<short,short> &a = it->second;
std::pair<short,short> z={1,2};
a.insert(z);
}
Out of that your title "compiler doesn't finish process" is not very clear, the execution is not the one you expect, but the compiler does not run the process
If multimap does not have an item with key 0, then multimap.find(0) returns an iterator that is not dereferenceable. Always check the return values of such calls before dereferencing the iterator.
auto iter = multimap.find(0);
if ( iter != multimap.end() )
{
std::multimap<short,short> &a = iter->second;
std::pair<short,short> z={1,2};
a.insert(z);
}
else
{
// Decide what to do
}
In my application I need the ability to traverse a doubly linked list starting from any arbitrary member of the list and continuing past the end(), wrapping around to the begin() and continue until the traversal reaches where it started.
I decided to use std::list for the underlying data structure and wrote a circulate routine to achieve this. However it's showing certain unexpected behavior when it's wrapping around from end() to begin(). Here's my implementation
template <class Container, class BiDirIterator>
void circulate(Container container, BiDirIterator cursor,
std::function<void(BiDirIterator current)> processor)
{
BiDirIterator start = cursor;
do {
processor(cursor);
cursor++;
if (cursor == container.end()) {
cursor = container.begin(); // [A]
}
} while (cursor != start);
}
// ...
typedef int T;
typedef std::list<T> TList;
typedef TList::iterator TIter;
int count = 0;
TList l;
l.push_back(42);
circulate<TList, TIter>(
l, l.begin(),
[&](TIter cur) {
std::cout << *cur << std::endl;
count++;
}
);
The output is:
42
-842150451
When I step through the code I see that the line marked [A] is never reached. The cursor is never equal to container.end(). Surprisingly, invoking ++ on that cursor, takes it to container.begin() in next pass automatically. (I suppose that's specific to this STL implementation).
How can I fix this behavior?
The issue here is that you are taking Container by value. This causes a copy so the iterators returned by container.end() and container.begin() are not that same as the iterator passed to the function. Instead if you pass Container by reference then the code works correctly.
Live Example
I am making a program for class that manages a Hotel. I am able to successfully check-in a customer into a room. But when I try to check-out a customer from a room, I get a run-time error: vector iterator incompatible. I ran the debugger, and says the problem is in the condition statement of my while loop, but I cant figure out what the problem is (I think I used the debugger correctly). I tried looking at other post with this similar error but I was not able to find a solution. Can anyone help?
void Customer::removeRoomID(int rID)
{
vector<int>::iterator iter;
iter = roomsCheckedInto.begin();
while(iter != roomsCheckedInto.end()) // <--DEBUGGER SAYS ERROR IN THIS LINE - ERROR: VECTOR ITERATOR INCOMPATIBLE
{
if(*iter==rID)
{
roomsCheckedInto.erase(iter);
}
}
}
std::vector iterators are invalidated once an erase operation has been performed. (See reference here)
Try changing your code to:
void Customer::removeRoomID(int rID)
{
vector<int>::iterator iter;
iter = roomsCheckedInto.begin();
while(iter != roomsCheckedInto.end())
{
if(*iter==rID)
{
// iter should now be set to the value
// returned from the erase() method.
iter = roomsCheckedInto.erase(iter);
}
else
{
++iter;
}
}
}
You are not advancing your iterator anywhere.
You need to do ++iter at some point or your while loop will be endless.
Also don't forget .erase invalidates the iterator, so you can't simply advance after erase.
Do iter = roomsCheckedInto.erase(iter); in case of matching id.
Firstly, you have to increment iter somewhere, or you will never reach the end of roomsCheckedInto - that is you will have an infinite loop.
Secondly, erase invalidates iter.
Instead, replace your while loop with:
while(iter != roomsCheckedInto.end())
{
if(*iter==rID) iter = roomsCheckedInto.erase(iter);
++iter;
}
Also, is this homework? If so, tag as such =)
This question already has answers here:
Closed 11 years ago.
Possible Duplicates:
vector erase iterator
Problem with std::map::iterator after calling erase()
I am having a concern about a piece of my code that I have. I have components and an object which stores the components. The problem is during an update the component can tell to remove a component from a the object. But its called from another function.
void Object::update() { //using std::map here
for(ComponentMap::iterator i = components.begin(); i != components.end(); ++i) {
(*i).second->update();
}
}
void HealthComponent::update() {
if(health <= 0) object->removeComponent("AliveComponent"); //this is wrong logic. but its just an example :D
}
void Object::removeComponent(string component) {
ComponentMap::iterator i = components.find(component);
if(i == components.end()) return;
components.erase(i);
}
and suppose I have lots of components - Health, Alive, Graphics, Physics, Input etc.
I tried something like this (with some test components) and no errors during during update. But I am really concerned. Can it pop me an error in the future? If yes, how to fix it?
Thanks in advance,
Gasim
You cannot loop through your container and say ++i when i is potentially no longer valid (because you erased it). A typical erase loop goes like this:
for (it = x.begin(); it != x.end(); /* nothing here! */)
{
if (must_erase(*it))
{
x.erase(it++); // advance it while still valid, return previous and erase
}
else
{
++it;
}
}
Rewrite your code in this spirit.
To spell out your problem: In Object::update(), you call HealthComponent::update() which invalidates the iterator i, and then you call ++i, which is undefined behaviour.
In MSVC erase will return the next valid iterator however in GCC it returns void so the only portable way to deal with this issue is keeping the previous iterator, erasing the current element then incrementing the previous iterator for next iteration.
http://www.cplusplus.com/reference/stl/map/erase/
void Object::removeComponent(string component, ComponentMap::iterator& _prev )
{
ComponentMap::iterator i = components.find(component);
if(i == components.end())
return;
_prev = i;
--_prev;
components.erase(i);
++prev;
}
I recently finished fixing a bug in the following function, and the answer surprised me. I have the following function (written as it was before I found the bug):
void Level::getItemsAt(vector<item::Item>& vect, const Point& pt)
{
vector<itemPtr>::iterator it; // itemPtr is a typedef for a std::tr1::shared_ptr<item::Item>
for(it=items.begin(); it!=items.end(); ++it)
{
if((*it)->getPosition() == pt)
{
item::Item item(**it);
items.erase(it);
vect.push_back(item);
}
}
}
This function finds all Item objects in the 'items' vector that has a certain position, removes them from 'items', and puts them in 'vect'. Later, a function named putItemsAt does the opposite, and adds items to 'items'. The first time through, getItemsAt works fine. After putItemsAt is called, though, the for loop in getItemsAt will run off the end of 'items'. 'it' will point at an invalid Item pointer, and getPosition() segfaults. On a hunch, I changed it!=items.end() to it<items.end(), and it worked. Can anyone tell me why? Looking around SO suggests it might involve erase invalidating the iterator, but it still doesn't make sense why it would work the first time through.
I'm also curious because I plan to change 'items' from a vector to a list, since list's erase is more efficient. I know I'd have to use != for a list, as it doesn't have a < operator. Would I run into the same problem using a list?
When you call erase(), that iterator becomes invalidated. Since that is your loop iterator, calling the '++' operator on it after invalidating it is undefined behavor. erase() returns a new valid iterator that points to the next item in the vector. You need to use that new iterator from that point onwards in your loop, ie:
void Level::getItemsAt(vector<item::Item>& vect, const Point& pt)
{
vector<itemPtr>::iterator it = items.begin();
while( it != items.end() )
{
if( (*it)->getPosition() == pt )
{
item::Item item(**it);
it = items.erase(it);
vect.push_back(item);
}
else
++it;
}
}
You're invoking undefined behavior. All the iterators to a vector are invalidated by the fact that you called erase on that vector. It's perfectly valid for an implementation to do whatever it wants.
When you call items.erase(it);, it is now invalid. To conform to the standard, you must now assume that it is dead.
You invoke undefined behavior by using that invalid iterator in the next call to vect.push_back.
You invoke undefined behavior again by using it as the tracking variable of your for loop.
You can make your code valid by using std::remove_copy_if.
class ItemIsAtPoint : std::unary_function<bool, item::Item>
{
Point pt;
public:
ItemIsAtPoint(const Point& inPt) : pt(inPt) {}
bool operator()(const item::Item* input)
{
return input->GetPosition() == pt;
}
};
void Level::getItemsAt(vector<item::Item>& vect, const Point& pt)
{
std::size_t oldSize = items.size();
std::remove_copy_if(items.begin(), items.end(), std::back_inserter(vect),
ItemIsAtPoint(pt));
items.resize(vect.size() - (items.size() - oldSize));
}
You can make this a lot prettier if you are using boost::bind, but this works.
I'll go with Remy Lebeau's explanation about iterator invalidation, and just add that you can make your code valid and asymptotically faster (linear time, instead of quadratic time) by using a std::list instead of a std::vector. (std::list deletions only invalidate the iterator that was deleted, and insertions don't invalidate any iterators.)
You can also predictibly identify iterator invalidation while debugging by activating your STL implementation's debug mode. On GCC, you do with with the compiler flag -D_GLIBCXX_DEBUG (see some caveats there).