2009-06-04 10 views
21

Tenir compte:map.erase (map.end())?

#include <map> 

int main() 
{ 
    std::map< int, int > m; 
    m[ 0 ] = 0; 
    m[ 1 ] = 1; 

    m.erase(0); // ok 
    m.erase(2); // no-op 
    m.erase(m.find(2)); // boom! 
} 

(. D'accord, les pourparlers de titre abouting effacer une fin() iterator, mais trouver retournera fin() pour une clé inexistante)

Pourquoi effacement d'un non -Clé valide OK, mais l'effacement final() explose. Je ne pouvais pas voir une mention explicite de cela dans la norme?

J'ai essayé ceci sur VS2005 (lève une exception dans la configuration de débogage) et GCC 4.0.1 (100% CPU). Est-ce que la mise en œuvre dépend

Merci.

Répondre

30

Pour erase(key), la norme indique que tous les éléments avec une clé de valeur sont supprimés. Il se peut bien sûr qu'il n'y ait pas de telles valeurs.

For erase(it) (où it est un std::map::iterator), la norme indique que l'élément pointé par celui-ci est retiré - malheureusement, si elle est end() il ne pointe pas vers un élément valide et que vous êtes hors de comportement non défini des terres , comme vous le feriez si vous utilisiez end() pour toute autre opération de carte. Voir la section 23.1.2 pour plus de détails.

+3

Pour clarifier: il existe différentes surcharges de erase(), et la version de l'itérateur nécessite un élément valide. – rlbond

+12

effacer (it) est équivalent à effacer (it, ++ iterator (it)), ce qui m'aide à voir que erase (it) n'est pas valide avec it = map.end(). Vous auriez besoin d'un autre itérateur après .end(). –

+0

Quelqu'un peut-il fournir un lien vers la norme? –

18

end() n'est pas un interacteur dans la carte. C'est effectivement «un passé la fin» de la carte.

La version 'itérateur' veut un itérateur à quelque chose dans la carte. La version 'clé' de erase fait la recherche et se protège contre la clé non trouvée, la version de l'itérateur suppose que vous n'essayez pas de casser des choses.

+0

"suppose que vous n'essayez pas de casser des choses" ... Je comprends, j'espérais que l'effacement ferait une simple vérification que cela! = Fin() –

+0

Pas une pensée déraisonnable, c'est juste que beaucoup de parties des conteneurs STL ne veulent pas les frais généraux de ces contrôles. Dans des cas comme l'effacement, l'itérateur est probablement utilisé pour autre chose avant que vous vouliez effacer l'entrée, donc ils omettent la vérification pour la fin - parce que vous l'avez probablement déjà fait. Dans le cas des clés, il est plus probable que l'appelant ne sache pas si la clé est sur la carte ou non, alors il effectue la vérification. –

+0

Ajout au commentaire précédent, juste pour mieux comprendre la carte: il s'agit d'un arbre de recherche binaire (la norme le requiert, généralement implémenté en arbre rouge-noir), et pour effacer une clé, il faut d'abord la trouver de toute façon, qu'elle existe ou pas - donc on est en train de faire une opération O (log n)) et accepter une clé inexistante n'implique aucun travail supplémentaire (comme le ferait le contrôle en erase (it)), il est donc logique de l'accepter en entrée. – Aconcagua

1

Voici un exemple rapide de la façon dont j'utilise la carte STL avec des itérateurs lors de la suppression. Je fais aussi la même chose lors de l'insertion. Personnellement, j'aime utiliser typedef pour définir spécifiquement la carte, mais le choix vous appartient.


typedef std::map... MapType; 

MapType the_map; 

MapType::iterator it = the_map.find ("key"); 
if (it != the_map.end()) { 
    // Do something productive. 
    the_map.erase (it); 
} 

MapType::iterator it = the_map.find ("new_key"); 

// Does not exist. 
if (it == the_map.end()) { 
    the_map.insert (std::make_pair ("new_key", 10)); 
} 

Espérons que cela aide!

3

Au lieu de l'exemple donné dans un précédent post ...

MapType::iterator it = the_map.find ("new_key"); 

// Does not exist. 
if (it == the_map.end()) { 
    the_map.insert (std::make_pair ("new_key", 10)); 
} 

qui fait deux parcours d'arbres, utilisation ...

pair<MapType::iterator, bool> rc = the_map.insert(make_pair("new_key", 0)); 
if (rc.second) 
    rc.first.second = 10; 

De cette façon, vous faire un traversal d'arbre et vous avez l'itérateur prêt à rouler pour d'autres choses.