Wie entferne ich mich von einer Karte, wenn ich sie iteriere? mögen:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
Wenn ich map.erase
verwende, werden die Iteratoren ungültig
Die standardmäßige Löschungssprache für assoziative Container:
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;
}
}
Beachten Sie, dass wir hier wirklich eine normale for
-Schleife wünschen, da wir den Container selbst ändern. Die bereichsbasierte Schleife sollte unbedingt für Situationen reserviert sein, in denen uns nur die Elemente interessieren. Die Syntax für die RBFL macht dies klar, indem der Container nicht einmal innerhalb des Schleifenkörpers freigelegt wird.
Bearbeiten. Vor C++ 11 konnten Sie Const-Iteratoren nicht löschen. Da müsstest du sagen:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Das Löschen eines Elements aus einem Container steht nicht im Widerspruch zur Konstanz des Elements. Analog dazu war delete p
immer absolut legitim, wobei p
ein Zeiger auf Konstante ist. Konstanz beschränkt die Lebensdauer nicht; const-Werte in C++ können weiterhin angehalten werden.
Ich persönlich bevorzuge dieses Muster, das etwas klarer und einfacher ist, auf Kosten einer zusätzlichen Variablen:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
Vorteile dieses Ansatzes:
it
und next_it
während der gesamten Iteration unverändert. Dadurch können Sie problemlos zusätzliche Anweisungen hinzufügen, die sich auf sie beziehen, ohne zu verraten, ob sie wie beabsichtigt funktionieren (außer, dass Sie it
nicht verwenden können es löschen).Kurz gesagt "Wie entferne ich mich von einer Karte, während ich sie durchläuft?"
Aus der GCC-Karte impl (note 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
Beispiel mit altem und neuem Stil:
#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;
}
druckt:
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.
Ziemlich traurig, wie? Normalerweise baue ich einen Container mit Iteratoren auf, anstatt ihn beim Durchlauf zu löschen. Dann durchlaufen Sie den Container und verwenden Sie 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)
}
Angenommen, C++ 11 ist hier ein einzeiliger Schleifenkörper, wenn dies Ihrem Programmierstil entspricht:
using Map = std::map<K,V>;
Map map;
// Erase members that satisfy needs_removing(itr)
for (Map::const_iterator itr = map.cbegin() ; itr != map.cend() ; )
itr = needs_removing(itr) ? map.erase(itr) : std::next(itr);
Ein paar andere kleinere Stiländerungen:
Map::const_iterator
) an, wenn möglich/bequem, mit auto
.using
für Vorlagentypen, damit Hilfstypen (Map::const_iterator
) einfacher lesbar/wartbar sind.