【问题标题】:Iterate over std::multimap and delete certain entries遍历 std::multimap 并删除某些条目
【发布时间】:2014-07-31 06:26:46
【问题描述】:

我想遍历 std::multimap 中的所有项目(所有键的所有值),并删除所有满足某些条件的条目:

#include <map>

typedef int KEY_TYPE;
typedef int VAL_TYPE;

bool shouldRemove(const KEY_TYPE&, const VAL_TYPE&);

void removeFromMap(std::multimap<KEY_TYPE,VAL_TYPE>& map){
    for (auto it = map.begin(); it != map.end(); it++){
        if (shouldRemove(it->first,it->second))
            map.erase(it);
    }
}

除非第一个项目被删除,否则迭代将起作用,然后引发以下错误:

map/set 迭代器不可递增

如何重写removeFromMap 函数才能正常工作?该代码应适用于地图的各种键值类型。

我正在使用 C++ 11 和 Visual Studio 2013。

【问题讨论】:

  • 通常使用map.erase(iterator)的返回值,因为它将迭代器返回到下一个元素或结束,如果它是最后一个元素
  • @Erbureth 您应该将其添加为答案:-)
  • @KarlNicoll 但是在深入挖掘之后,Erase-remove 习语不适用于std::setstd::map 和朋友,因为它们的值类型不是MoveAssignable。而且我不知道如何在这样的容器上实现它,因为它通过移动元素来工作。

标签: c++ c++11 std


【解决方案1】:

您需要在进行擦除之前增加您的迭代器。当您执行 map.erase(it); 时,迭代器 it 变得无效。但是,地图中的其他迭代器仍然有效。因此,您可以通过对迭代器进行后增量来解决此问题...

auto it = map.begin();
const auto end = map.end();

while (it != end)
{
    if (shouldRemove(it->first,it->second))
    {
        map.erase(it++);
                 // ^^ Note the increment here.
    }
    else
    {
       ++it;
    }
}

map.erase() 参数内应用到it 的后增量将确保it 在项目被删除后保持有效,方法是在删除前递增迭代器以指向映射中的下一个项目。

map.erase(it++);

...在功能上等同于...

auto toEraseIterator = it;    // Remember the iterator to the item we want to erase.
++it;                         // Move to the next item in the map.
map.erase(toEraseIterator);   // Erase the item.

正如@imbtfab 在 cmets 中指出的那样,您也可以使用 it = map.erase(it) 在 C++11 中做同样的事情,而无需后递增。

另请注意,for 循环现在已更改为 while 循环,因为我们手动控制迭代器。

此外,如果您希望使 removeFromMap 函数尽可能通用,则应考虑使用模板参数并直接传递迭代器,而不是传递对多映射的引用。这将允许您使用任何地图样式的容器类型,而不是强制输入 multimap

例如

template <typename Iterator>
void removeFromMap(Iterator it, const Iterator &end){
    ...
}

这也是标准 C++ &lt;algorithm&gt; 函数的执行方式(例如 std::sort(...))。

【讨论】:

  • 这对我有用,谢谢!但是,我不明白为什么使用后增量方式可以解决问题 - 为什么erase(it); it++;erase(it++); 不同?
  • 当您执行erase(it) 时,变量it 不再可用。在删除it 后尝试增加它是未定义的行为。通过执行erase(it++),您可以增加it 擦除被调用,但仍将旧的未增加的迭代器传递给erase 函数。它等价于:auto newIt = std::next(it); map.erase(it); it = newIt;。通过seeing how the post-increment operator works,您可能会得到更好的理解。
  • @Karl:在您的评论中,您需要auto newIt = std::next(it)(或其他内容。您所拥有的是递增它并将递增的值分配给newIt。
  • 由于是C++11,避免混淆,直接使用it = map.erase(it);
  • @imbtfab - 您应该将其添加为另一个答案,而不是让我把所有功劳归于它:-)
猜你喜欢
  • 2015-05-18
  • 2014-04-12
  • 2012-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-03
  • 1970-01-01
相关资源
最近更新 更多