iteration over a C++ map giving infinite loop

拥有回忆 提交于 2021-01-28 01:08:48

问题


I have the following method in C++ that only removes the elements associated with a particular tableId from a map.

 69 void
 70 ObjectFinder::flush(uint64_t tableId) {
 71 
 72     RAMCLOUD_TEST_LOG("flushing object map");
 74     // find everything between tableId, 0
 75     // keep scanning util find all the entries for that table
 76     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::const_iterator it;
 79     for (it = tableMap.begin(); it != tableMap.end(); it++) {
 80         TabletKey current = it->first;
 81         if (tableId == current.first) {
 82             tableMap.erase(current);
 83         }
 84     }
 85     std::cout << "hello" << std::endl;
 87 }

Stepping into the code with gdb I found out that an infinite loop is happening after the iteration of the for loop. The line 85 is never printed out. I'm assuming a dangling pointer is happening. In the first loop, the current element is removed, then in the next two nothing happens, and then I have the infinite loop. I'm total clueless why is this happening. Does someone has an idea or has experienced it before?

Another smarter version of my code is to use lower_bound and upper_bound to find where the id begins (it would save some computing time):

 69 void
 70 ObjectFinder::flush(uint64_t tableId) {
 71 
 72     RAMCLOUD_TEST_LOG("flushing object map");
        KeyHash keyHash = Key::getHash(tableId, "", 0);
 74     // find everything between tableId, 0
 75     // keep scanning util find all the entries for that table
 76     std::map<TabletKey, ProtoBuf::Tablets::Tablet>::const_iterator lower;
        std::map<TabletKey, ProtoBuf::Tablets::Tablet>::const_iterator upper;
        TabletKey key(tableId, keyHash);

        lower = tableMap.lower_bound(key);
        upper = tableMap.upper_bound(key);
        tableMap.erase(lower, upper);
 85     std::cout << "hello" << std::endl;
 87 }

and I get:

/home/ribeiro.phillipe/ramcloud/src/ObjectFinder.cc:81: error: no matching function for call to ‘std::map<std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet, std::less<std::pair<long unsigned int, long unsigned int> >, std::allocator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> > >::erase(std::_Rb_tree_const_iterator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> >)’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_map.h:566: note: candidates are: void std::map<_Key, _Tp, _Compare, _Alloc>::erase(typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator) [with _Key = std::pair<long unsigned int, long unsigned int>, _Tp = RAMCloud::ProtoBuf::Tablets_Tablet, _Compare = std::less<std::pair<long unsigned int, long unsigned int> >, _Alloc = std::allocator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> >]
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_map.h:581: note:                 typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::size_type std::map<_Key, _Tp, _Compare, _Alloc>::erase(const _Key&) [with _Key = std::pair<long unsigned int, long unsigned int>, _Tp = RAMCloud::ProtoBuf::Tablets_Tablet, _Compare = std::less<std::pair<long unsigned int, long unsigned int> >, _Alloc = std::allocator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> >]
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_map.h:596: note:                 void std::map<_Key, _Tp, _Compare, _Alloc>::erase(typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator) [with _Key = std::pair<long unsigned int, long unsigned int>, _Tp = RAMCloud::ProtoBuf::Tablets_Tablet, _Compare = std::less<std::pair<long unsigned int, long unsigned int> >, _Alloc = std::allocator<std::pair<const std::pair<long unsigned int, long unsigned int>, RAMCloud::ProtoBuf::Tablets_Tablet> >]
make: *** [obj.master/ObjectFinder.o] Error 1

is that because I don't have a C++ version that support that?


回答1:


Your code has undefined behaviour because you're using an iterator that you've just invalidated. Do it like this:

for (it = tableMap.begin(); it != tableMap.end(); )
{
    if (tableId == it->first.first) { tableMap.erase(it++); }
    else                            { ++it; }
}



回答2:


tableMap.erase(current);

This line invalidates the iterator it. Using it afterwards yields undefined behaviour.

You need to advance the iterator before erasing that element. You would need to use something like tableMap.erase(it++); and then be careful to skip the regular loop increment.



来源:https://stackoverflow.com/questions/17654995/iteration-over-a-c-map-giving-infinite-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!