2008-12-09 6 views
2

J'ai un ensemble d'objets que je parcours, mais je peux décider au cours de l'itération qu'un (ou plusieurs) de ces objets doivent maintenant être supprimés.Meilleure façon de supprimer un élément in situ

Mon code est le suivant:

if(! m_Container.empty()) 
    { 
     for( typedefedcontainer::iterator it = m_Container.begin(); 
       it != m_Container.end(); 
       ++it ) 
     { 
      if(! (SomeFunction((*it), "test", "TEST!", false)) ) 
      { 
      // If function returns false, delete object. 
       m_Container.erase(it); 
       AsyncResponseStore::iterator it = m_asyncResponses.begin(); 
      } 

     } 


    } 

Mais bien sûr, quand j'effacer un objet que j'obtiens une erreur: « Carte/set iterator pas incrémentable ». Quelqu'un peut-il suggérer une meilleure façon de le faire?

Voir: What happens if you call erase() on a map element while iterating from begin to end?

+0

Pourquoi testez-vous le vide de votre conteneur? Si elle est vide, la boucle for ne sera jamais entrée. –

Répondre

6

Cela dépend du récipient. Le conteneur de liste prend en charge la suppression pendant l'énumération en renvoyant un nouvel itérateur à partir de la méthode d'effacement qui représente l'élément suivant dans la liste. La carte ne supporte pas cela.

Une méthode simple pour la carte est d'accumuler les éléments que vous voulez effacer dans une liste séparée, puis de parcourir cette liste lorsque vous avez terminé le traitement de la carte pour effacer les éléments de la carte. Cela suppose que vous pouvez différer la suppression jusqu'à la fin de l'itération. Si ce n'est pas le cas, vous n'avez pas d'autre choix que de redémarrer l'itération pour chaque suppression.

0

fixe par ce qui suit:

for( typedefedcontainer::iterator it = m_Container.begin(); 
     it != m_Container.end(); 
     ) 
{ 
    if(! (SomeFunction((*it), "test", "TEST!", false)) ) 
    { 
    // If function returns false, delete object. 
     m_Container.erase(it++); 
    } 
    else 
    { 
     ++i; 
    } 

} 

Lorsqu'un élément est supprimé, tous les pointeurs vers celui-ci deviennent invalidée. Par conséquent, en l'utilisant ++ nous le contournerons. Merci à ceux qui ont posté des suggestions.

+0

Tout simplement parce que vous passez en sécurité à l'itérateur suivant, ** ne signifie pas ** que cet itérateur est valide. onebyone a la meilleure solution. –

+0

Bien que pour certains types de conteneurs, cela signifie que.Comme le dit Rob Walker, ce dont vous pouvez vous passer dépend du type de conteneur. C'est pourquoi std :: remove_if existe, car avec erase cela fonctionne sur tout ce qui a des itérateurs mutables. –

6

Si le conteneur supporte (que je soupçonne que le vôtre ne pas, mais le titre de la question est générique si cela peut être utile à d'autres, sinon vous):

struct SomePredicate { 
    bool operator()(typedefedcontainer::value_type thing) { 
     return ! SomeFunction(thing, "test", "TEST", false); 
    } 
}; 

typedefedcontainer::iterator it; 
it = std::remove_if(m_Container.begin(), m_Container.end(), SomePredicate()); 
m_Container.erase(it, m_Container.end()); 

m_Container doit avoir une méthode de plage d'effacement , qui comprend toute séquence ou conteneur associatif. Il doit cependant avoir un itérateur mutable, et je viens de remarquer que j'ai mal lu le message d'erreur: il indique "map/set iterator not incrementable". Donc je suppose que votre conteneur est une carte ou un ensemble. Notez que les trois derniers pourraient être un très beau doublage, mais cette marge est trop étroite pour le contenir.

De même que SomePredicate pourrait avoir un constructeur avec des paramètres pour stocker les paramètres supplémentaires à SomeFunction, puisque dans la vraie vie, je suppose qu'ils ne sont pas constants.

Vous pouvez réellement vous débarrasser complètement de SomePredicate si vous utilisez boost: bind pour construire le foncteur. Votre one-liner serait alors vraiment énorme. [Edit: Rob Walker note correctement dans sa réponse une hypothèse que je fais ici et que la question ne précise pas, c'est-à-dire que tout effacement peut être différé jusqu'à ce que l'itération-et-test soit fait. Si SomeFunction accède à m_Container par une route cachée (par exemple un global, ou parce que SomeFunction en est une fonction membre) et que ses résultats dépendent du contenu du conteneur, mon code peut ne pas être équivalent au code de l'interrogateur. Mais je pense que mon code est la valeur par défaut "à moins qu'il y ait une raison de ne pas".]

Questions connexes