222 votes

Comment faire pour supprimer à partir d'une carte lors de l'itération?

Comment puis-je supprimer à partir d'une carte lors de l'itération? comme:

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

Si j'utilise map.erase , elle invalide les itérateurs

336voto

Kerrek SB Points 194696

La norme associative-conteneur effacer idiome:

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

Notez que nous voulons vraiment de l'ordinaire, for boucle ici, puisque nous sommes en train de modifier le conteneur lui-même. La gamme à base de boucle doit être strictement réservée aux situations où nous ne se soucient que les éléments. La syntaxe de la RBFL est clair à ce sujet par même pas exposer le conteneur à l'intérieur du corps de la boucle.

Edit. Avant C++11, on ne pouvait pas effacer const-les itérateurs. Il vous faudrait dire:

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

L'effacement d'un élément à partir d'un conteneur n'est pas en contradiction avec constness de l'élément. Par analogie, il a toujours été parfaitement légitime delete pp est un pointeur vers une constante. En d'autres termes encore, le destructeur est toujours implicitement const.

8voto

thekashyap Points 1636

En bref "Comment puis-je supprimer à partir d'une carte lors de l'itération?"

  • Avec l'ancienne carte impl: Vous ne pouvez pas
  • Avec la nouvelle carte de impl: presque comme @KerrekSB suggéré. Mais il y a quelques problèmes de syntaxe dans ce qu'il a posté.

De GCC carte 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

Exemple avec l'ancien et le nouveau style:

#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;
}

impressions:

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.

4voto

Michael Daum Points 589

Assez triste, hein? La façon dont j'ai l'habitude de le faire est de construire un conteneur de itérateurs au lieu de les supprimer lors de la traversée. Ensuite une boucle à travers le contenant et l'utilisation de la carte.effacer()

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)
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X