2014-06-10 41 views
1

我想所有项目迭代中std::multimap(所有所有键的值),并删除满足某个条件的所有条目:遍历的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); 
    } 
} 

迭代工程除非第一个项目被删除,并且抛出了以下错误,则:

地图/套迭代器不递增的

哪有removeFromMap函数被重写以正常工作?该代码应该适用于地图的各种键和值类型。

我使用C++ 11和Visual Studio 2013年

+0

通常,使用map.erase(iterator)的返回值,因为它会将迭代器返回到下一个元素或结束,如果它是最后一个元素 – AquilaRapax

+0

@Erbureth您应该将其添加为答案:-) –

+0

@ KarlNicoll然而,深入挖掘之后,Erase-remove成语不适用于'std :: set','std :: map'和朋友,因为它们的值类型不是'MoveAssignable'。我不知道如何在这种容器上实现它,因为它通过转换元素来工作。 – Erbureth

回答

5

你需要增加你的迭代你做擦除之前。当你做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; 
    } 
} 

应用于itmap.erase()参数内的后加将确保该项目是通过增加迭代消去后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在评论中指出的那样,你也可以用it = map.erase(it)做在C++ 11的同样的事情,而不需要后增加。

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

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

例如,

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

这是标准的C++函数<algorithm>如何做到这一点也(例如std::sort(...))。

+0

适合我,谢谢!但是,我不明白为什么使用后增加的方式可以修复问题 - 为什么要“擦除”(它);它++;'和'擦除(它++);'不同? – muffel

+1

当你擦除(它)时,变量'it'不再可用。试图在擦除之后增加“it”是*未定义的行为*。通过执行'erase(it ++)',你可以在* erase被调用之前递增'it' *,但仍然会将旧的未递增的迭代器传递到'erase'函数中。它相当于以下内容:'auto newIt = std :: next(it); map.erase(它); it = newIt;'。你可以通过[看到后增量操作符是如何工作的]来更好地理解(https://stackoverflow.com/questions/484462/difference-between-i-and-i-in-a-loop)。 –

+0

@Karl:在你的评论中,你想要'auto newIt = std :: next(it)'(或其他的东西)。你有什么增加它并将增加的值赋给newIt。 –