问题
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