2

This method causes an abort error: "map/set iterator not incrementable." Due to that after the if fails and a vaild iterator that should be erased is determined, (and is), continuing to the next iterator in the map via ++_iter fails because _iter is no longer a valid object/pointer.

What is the correct procedure for iterating through a map AND having the ability to remove individual items throughout?

typedef std::map<std::string, BITMAP*> MapStrBmp; typedef MapStrBmp::iterator MapStrBmpIter; \\... void BitmapCache::CleanCache() { //Clean the cache of any NULL bitmaps that were deleted by caller. for(MapStrBmpIter _iter = _cache.begin(); _iter != _cache.end(); ++_iter) { if(_iter->second != NULL) { if((_iter->second->w < 0 && _iter->second->h < 0) == false) continue; } _cache.erase(_iter); } } 
4

4 Answers 4

6

You just have to be a bit more careful:

void BitmapCache::CleanCache() { //Clean the cache of any NULL bitmaps that were deleted by caller. for(MapStrBmpIter _iter = _cache.begin(); _iter != _cache.end(); ) { if(_iter->second != NULL) { if((_iter->second->w < 0 && _iter->second->h < 0) == false) { ++_iter; continue; } } _cache.erase(_iter++); } } 
Sign up to request clarification or add additional context in comments.

5 Comments

Would you mind explaining with words the changes you made, please, so we don't have to play "spot the differences"?
Why is incrementing the iterator inline in the erase command differnt to it be incremented on the next for loop? Just curious…
@v01pe: It's incremented before the call to erase invalidates it.
@v01pe: it's a postfix increment, so it increments the iterator to point to the next element, then passes a copy of the old iterator (that is to the item to be deleted) to _cache.erase. map::erase invalidates only iterators to elements that it actually deletes, so the current value of _iter remains valid and it points to the next element.
@ybungalobill: I know that it's a postfix increment, but I didn't know, that a copy of the old value (iterator in that case) is evaluated. I thought the value it self is passed, and will be incremented at the end (not upfront) of the expression. Good to know!
2

map::erase(iterator) gives you an iterator pointing to the next element in the map (if any) after erasing. Therefore, you can do:

for(MapStrBmpIter _iter = _cache.begin(); _iter != _cache.end(); ) { if(_iter->second != NULL) { if((_iter->second->w < 0 && _iter->second->h < 0) == false) { ++_iter; continue; } } _iter = _cache.erase(_iter); } 

2 Comments

Unfortunately it's only since C++11. C++03 version returns void.
I think this is not even valid in C++11. The function prototype for erase in C++11 looks like this iterator erase( const_iterator pos ).
1

The standard erase loop for an associative container:

for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */) { if (delete_condition) { m.erase(it++); } else { ++it; } } 

Comments

0

The canonical way to safely erase iterators during an iteration is to use the result of container::erase:

void BitmapCache::CleanCache() { //Clean the cache of any NULL bitmaps that were deleted by caller. MapStrBmpIter _iter = _cache.begin(); while (_iter != _cache.end()) { bool erase_entry= true; if(_iter->second != NULL) { if((_iter->second->w < 0 && _iter->second->h < 0) == false) erase_entry= false; } if (erase_entry) _iter= _cache.erase(_iter); else ++_iter; } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.