【问题标题】:iteration over a C++ map giving infinite loop在 C++ 映射上迭代给出无限循环
【发布时间】:2013-07-15 13:05:52
【问题描述】:

我在C++ 中有以下方法,它只从地图中删除与特定tableId 关联的元素。

 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 }

使用gdb 进入代码我发现在for 循环的迭代之后发生了无限循环。 85 行永远不会被打印出来。我假设一个悬空指针正在发生。在第一个循环中,current 元素被删除,然后在接下来的两个循环中没有任何反应,然后我进入了无限循环。 我完全不知道为什么会发生这种情况。有人有想法或以前经历过吗?

我的代码的另一个更智能的版本是使用lower_boundupper_bound 来查找id 的开始位置(这样可以节省一些计算时间):

 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 }

我得到:

/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

是因为我没有支持它的C++ 版本吗?

【问题讨论】:

  • 从地图中删除元素时,迭代器无效。您可以使用 while 循环 tableMap.erase(current++)(后增量很重要)

标签: c++ map infinite-loop


【解决方案1】:

您的代码具有未定义的行为,因为您使用的是刚刚失效的迭代器。这样做:

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

【讨论】:

  • Kerrek,我可以继续问你另一个问题吗?它与这个问题有关,但我没有在地图上循环,而是使用 upper_bound 和一个键来查找它从哪里开始
  • @philippe:编辑你的问题,我会尽力解决。
  • 我在使用您源代码中的iterator 时遇到了同样的问题。
【解决方案2】:
tableMap.erase(current);

这条线invalidates the iteratorit。之后使用它会产生未定义的行为。

您需要在删除该元素之前推进迭代器。您需要使用 tableMap.erase(it++); 之类的东西,然后小心跳过常规循环增量。

【讨论】:

  • R. Martinho,关于tableMap.erase(tableId);的更多细节?
  • 我认为密钥类型是一对...?
  • @philippe 哦,算了。我误读了代码,没有注意到钥匙是一对。谢谢克雷克。
  • 是的,密钥类型是一对
  • @R.MartinhoFernandes 我也更新了我的问题...请看一下。
猜你喜欢
  • 1970-01-01
  • 2011-07-06
  • 2014-10-31
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 2021-04-15
相关资源
最近更新 更多