web-dev-qa-db-de.com

Wie entferne ich es von einer Karte, wenn ich es iteriere?

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

137
Dani

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.

221
Kerrek SB

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:

  • der for-Schleife-Inkrementor ist als Inkrementor sinnvoll;
  • die Löschoperation ist eine einfache Löschung und wird nicht mit der Inkrementierungslogik gemischt.
  • nach der ersten Zeile des Schleifenkörpers bleibt die Bedeutung von 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).
14
D Coetzee

Kurz gesagt "Wie entferne ich mich von einer Karte, während ich sie durchläuft?"

  • Mit alter Map impl: Das geht nicht
  • Mit neuer Karte impl: fast wie @ KerrekSB vorgeschlagen. Es gibt jedoch einige Syntaxprobleme in dem, was er gepostet hat.

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.
4
Kashyap

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)
}
3
Michael Daum

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:

  • Zeigt den deklarierten Typ (Map::const_iterator) an, wenn möglich/bequem, mit auto.
  • Verwenden Sie using für Vorlagentypen, damit Hilfstypen (Map::const_iterator) einfacher lesbar/wartbar sind.
0
Matt