如何从迭代中删除地图?

如何在迭代时从地图中删除? 喜欢:

std::map<K, V> map; for(auto i : map) if(needs_removing(i)) // remove it from the map 

如果我使用map.erase它将使迭代器无效

标准关联容器擦除习惯用法:

 for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */) { if (must_delete) { m.erase(it++); // or "it = m.erase(it)" since C++11 } else { ++it; } } 

请注意,我们确实需要一个普通的for循环,因为我们正在修改容器本身。 范围为基础的循环应严格保留在我们只关心元素的情况。 RBFL的语法通过甚至不暴露循环体内的容器来解决这个问题。

编辑。 Pre-C ++ 11,你无法擦除常量迭代器。 在那里你不得不说:

 for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ } 

从容器中删除一个元素与元素的常量并不矛盾。 通过类推, delete pp是一个指向常量的指针)总是合法的。 保持不限制一生; C ++中的const值仍然可以停止存在。

很伤心,呃? 我通常这样做的方式是build立一个迭代器的容器,而不是在遍历期间删除。 然后遍历容器并使用map.erase()

 std::map<K,V> map; std::list< std::map<K,V>::iterator > iteratorList; for(auto i : map ){ if ( needs_removing(i)){ iteratorList.push_back(i); } } for(auto i : iteratorList){ map.erase(*i) } 

简而言之,“如何在迭代时从地图中删除?”

  • 用旧地图impl:你不行
  • 用新地图im​​pl:几乎像@KerrekSBbuild议的那样。 但是他发布的内容中有一些语法问题。

从GCC地图impl(注意GXX_EXPERIMENTAL_CXX0X ):

 #ifdef __GXX_EXPERIMENTAL_CXX0X__ // _GLIBCXX_RESOLVE_LIB_DEFECTS // DR 130. Associative erase should return an iterator. /** * @brief Erases an element from a %map. * @param position An iterator pointing to the element to be erased. * @return An iterator pointing to the element immediately following * @a position prior to the element being erased. If no such * element exists, end() is returned. * * This function erases an element, pointed to by the given * iterator, from a %map. Note that this function only erases * the element, and that if the element is itself a pointer, * the pointed-to memory is not touched in any way. Managing * the pointer is the user's responsibility. */ iterator erase(iterator __position) { return _M_t.erase(__position); } #else /** * @brief Erases an element from a %map. * @param position An iterator pointing to the element to be erased. * * This function erases an element, pointed to by the given * iterator, from a %map. Note that this function only erases * the element, and that if the element is itself a pointer, * the pointed-to memory is not touched in any way. Managing * the pointer is the user's responsibility. */ void erase(iterator __position) { _M_t.erase(__position); } #endif 

新旧风格的例子:

 #include <iostream> #include <map> #include <vector> #include <algorithm> using namespace std; typedef map<int, int> t_myMap; typedef vector<t_myMap::key_type> t_myVec; int main() { cout << "main() ENTRY" << endl; t_myMap mi; mi.insert(t_myMap::value_type(1,1)); mi.insert(t_myMap::value_type(2,1)); mi.insert(t_myMap::value_type(3,1)); mi.insert(t_myMap::value_type(4,1)); mi.insert(t_myMap::value_type(5,1)); mi.insert(t_myMap::value_type(6,1)); cout << "Init" << endl; for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++) cout << '\t' << i->first << '-' << i->second << endl; t_myVec markedForDeath; for (t_myMap::const_iterator it = mi.begin(); it != mi.end() ; it++) if (it->first > 2 && it->first < 5) markedForDeath.push_back(it->first); for(size_t i = 0; i < markedForDeath.size(); i++) // old erase, returns void... mi.erase(markedForDeath[i]); cout << "after old style erase of 3 & 4.." << endl; for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++) cout << '\t' << i->first << '-' << i->second << endl; for (auto it = mi.begin(); it != mi.end(); ) { if (it->first == 5) // new erase() that returns iter.. it = mi.erase(it); else ++it; } cout << "after new style erase of 5" << endl; // new cend/cbegin and lambda.. for_each(mi.cbegin(), mi.cend(), [](t_myMap::const_reference it){cout << '\t' << it.first << '-' << it.second << endl;}); return 0; } 

打印:

 main() ENTRY Init 1-1 2-1 3-1 4-1 5-1 6-1 after old style erase of 3 & 4.. 1-1 2-1 5-1 6-1 after new style erase of 5 1-1 2-1 6-1 Process returned 0 (0x0) execution time : 0.021 s Press any key to continue. 

我个人比较喜欢这个稍微更清晰简单的模式,但是需要额外的variables:

 for (auto it = m.cbegin(), next_it = m.cbegin(); it != m.cend(); it = next_it) { next_it = it; ++next_it; if (must_delete) { m.erase(it); } } 

这种方法的优点:

  • for循环递增器作为递增器是有意义的;
  • 擦除操作是一个简单的擦除,而不是与增量逻辑混合;
  • 在循环体的第一行之后, itnext_it的含义在整个迭代next_it保持不变,这样您可以轻松地添加引用它们的其他语句,而不用担心它们是否按照预期工作(当然,除了在之后不能使用it擦除它)。

我认为在迭代过程中擦除地图元素的最明智的方法是(至less在C ++ 14中):

 for(auto it = m.begin(); it != m.end(); ++it) { if(delete_condition) { m.erase(it); } }