如何从迭代中删除地图?
如何在迭代时从地图中删除? 喜欢:
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 p
( p
是一个指向常量的指针)总是合法的。 保持不限制一生; 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:你不行
- 用新地图impl:几乎像@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循环递增器作为递增器是有意义的;
- 擦除操作是一个简单的擦除,而不是与增量逻辑混合;
- 在循环体的第一行之后,
it
和next_it
的含义在整个迭代next_it
保持不变,这样您可以轻松地添加引用它们的其他语句,而不用担心它们是否按照预期工作(当然,除了在之后不能使用it
擦除它)。
我认为在迭代过程中擦除地图元素的最明智的方法是(至less在C ++ 14中):
for(auto it = m.begin(); it != m.end(); ++it) { if(delete_condition) { m.erase(it); } }
- 在成员函数内的lambda捕获列表中使用成员variables
- 重载参考,而不是唯一的传值+ std :: move?
- 如何以编程方式确定在C ++中expression式是右值还是左值?
- std :: sort检查一个向量是否已经sorting?
- 通过重复对象名称来调用静态方法
- std :: to_string – 重载函数的实例超过参数列表
- 是否unique_ptr保证移动后存储nullptr?
- 在'auto f(params) – > decltype(…,void())'中的'void()'是做什么的?
- 为什么我们需要使用virtual〜A()= default; 而不是虚拟〜A(){}在C + + 11?