Click here to Skip to main content
15,884,177 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

I'm getting a run-time error when I try to iterate my std:map.

Here is the class. In the dtor, I need to clear the map
C++
class CMyClass
{
public:
   CMyClass()
   {

   }
   void PopulateMap()
   {
    // populate map
   }
   ~CMyClass()
   {
    // clear map
    CSourceToIdMap::iterator itEvent = m_EventMap.begin();
   // Line below ERROR
    while(itEvent != m_EventMap.end()) // RunTime Error : map/set iterators incompatible
    {
      CIdToTargetMap * pIdToTargetMap = itEvent->second;
      m_EventMap.erase(itEvent++);
      delete pIdToTargetMap;
    }
   }  
private:
  typedef std::multimap<int, CPanel *> CIdToTargetMap;
  typedef std::map<CPanel *, CIdToTargetMap *> CSourceToIdMap;
  CSourceToIdMap m_EventMap;

};

THE PROBLEM:

The problem does NOT seem to be that an iterating is pointing to an invalid element in the map.

The problem seems to be iterator being NOT compatible.

When run the program, I get an assert in this file:

c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\xtree

Line 321: _DEBUG_ERROR("map/set iterators incompatible");


I have tried all the suggestions already provided here. I still get the ASSERT.

Here is another link that discuss this issue:
http://stackoverflow.com/questions/3772304/runtime-error-map-set-iterators-incompatible[^]
But the solution mentioned in that link is not clear to me.


Any ideas/suggestions?
Thanks in advance.
Posted
Updated 30-Aug-13 2:28am
v3
Comments
Philippe Mori 30-Aug-13 8:27am    
Also see comment I have added to my solution. One reason that iterator might be incompatible is because they don't below to the same container and debug version do extra check to ensure that the code is correct (according to standard rules).

For the link RunTime Error : map/set iterators incompatible, it seems that the problem might be that the container was copied (according to the proposed answer). If so, it won't apply to your case.

Solution 1 may solve the issue. However, since this is in the destructor and all you're trying to do is clear up the memory, I'd stick with the forward iterator and simply remove the call to "erase". If you want to be neat about it, you could call "clear" at the end. Thus:
C++
~CMyClass()
{
 // clear map
 for (CSourceToIdMap::iterator itEvent = m_EventMap.begin();
      itEvent != m_EventMap.end(); ++itEvent)
 {
   delete itEvent->second;
 }
 m_EventMap.clear();  // not necessary, but for neatness
}

Regards,
Ian.
 
Share this answer
 
Comments
Philippe Mori 29-Aug-13 18:47pm    
This is effectively a good solution when all items are erased and you need to delete a subobject first.
H.Brydon 30-Aug-13 0:12am    
I agree, solution #1 and #2 are both correct, but #2 addresses the immediate problem better.
Mizan Rahman 30-Aug-13 3:53am    
I updated the question, please have another look.
Okay, I compiled and ran this fine with no runtime error (as a Win32 console application with some dummy classes for CPanel and CIdToTargetMap).

The only thing I can think of is that you have an earlier declaration of "CSourceToIdMap" with a different definition. Try changing the name of the type used within the class to something else (e.g. CSourceToIdMap2) to see if that works and fixes (and therefore confirms) the problem.

One other possibility is the order of the includes - perhaps there is something interfering with "map". Try reducing the number of includes to the bare minimum required, and play with the order of them. (Some of the Windows includes play havoc with the STL ones if they are included in the wrong order).

What version of the STL are you using?

Regards,
Ian.
 
Share this answer
 
[edit: I just realized you said it was a runtime error, not compile error...]

When you erase an item in a collection, the forward iterator is invalidated. What you want to do is delete your collection from the end of list to front using a reverse iterator. You would iterate from rbegin() to rend()...
 
Share this answer
 
v2
Comments
Philippe Mori 29-Aug-13 19:07pm    
I think it works because reverse iterators are offseted by 1. I don't think it would works with bidirectionnal iterators.
H.Brydon 30-Aug-13 0:11am    
No, it works because reverse iterators start from the end of the list, and if you delete the last element, it is at the end of the list, and the reverse iterator is still valid.

If you delete the first element with a (forward) iterator, all the remaining items in the iterator's context juggle down and the iterator becomes invalid.
Philippe Mori 30-Aug-13 8:11am    
See my comments in my own solution. I have found a few web pages that explain STL map iterator invalidation rules which are well defined. Your code would essentially be correct for vectors but different containers have different rules.
Mizan Rahman 30-Aug-13 3:53am    
I updated the question, please have another look.
You cannot erase the current item without updating the iterator first. So you must make a copy of the iterator to be able to erase an element inside the loop.
C++
CIdToTargetMap * pIdToTargetMap = itEvent->second;

// Advance iterator before erasing item
CSourceToIdMap::iterator itTemp = itEvent;
++itEvent;
m_EventMap.erase(itTemp);

delete pIdToTargetMap;

However, if all items are removed, then Ian solution is more appropriate.

Also as in Ian solution, it is not necessary to have a temporary variable pIdToTargetMap in this case as it does not affect exception safety. In more complex case, it might be useful to to it that way as it help ensure you won't access a deleted item afterward.
 
Share this answer
 
Comments
H.Brydon 30-Aug-13 0:15am    
This solution is not correct. If you delete anything "at or before" a forward iterator, the iterator becomes invalid. Making a second copy does not help you. It is invalid as well.
Philippe Mori 30-Aug-13 8:07am    
STL containers have well defined rules when iterators are invalidated. In the case of a map, only the iterator pointing to the element that is erased is invalidated:

map<> - CGI

Iterator invalidation rules

And by the way, here is a link about reverse iterator:
How to call erase with a reverse iterator.

Thus everything I have said was right.
Mizan Rahman 30-Aug-13 3:53am    
I updated the question, please have another look.
Philippe Mori 30-Aug-13 8:21am    
The reason why your code does not works is that when an element is removed from a map, links are rearranged to connect previous element with next element. The iterator for the current element is invalidated and thus calling ++ on it is undefined. In debug version (with checks enabled), an error might be reported (as the iterator does not belong to the container anymore). In release version, the behavior is undefined.

Have you tried any other proposed solution? In any case, you should start by using a solution that is correct according to STL rules (solution 2 or 3) and if you still have a problem, then it might be something else in your code that is wrong (like a double delete).

If you remove delete or remove erase call, do you still have the error? With Ian solution do you still have the error? And with mine?
Have you tried following?
1. Iterate through map and delete your all CIdToTargetMap pointers first.
2. use map::clear to erase the entries in the map.

I hope it helps.
 
Share this answer
 
Comments
H.Brydon 30-Aug-13 0:52am    
This sounds like a restatement of solution #2...
SandipG 30-Aug-13 1:01am    
Yes.. I need to read the other solutions carefully before posting.. thanks will take care next time..
Mizan Rahman 30-Aug-13 3:53am    
I updated the question, please have another look.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900