2009-06-04 3 views
21

considerar: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! 
} 

(. OK, por lo que las conversaciones título abouting borrar un fin() iterador, pero find devolverá final() para una clave inexistente)

Por qué está borrando un no -existente clave OK, pero borrando final() explota. No pude ver ninguna mención explícita de esto en el estándar?

He intentado esto con VS2005 (arroja una excepción en la configuración de depuración) y GCC 4.0.1 (100% CPU). ¿Depende de la implementación?

Gracias.

Respuesta

30

Para erase(key), la norma indica que todos los elementos con clave de valor se eliminan. Por supuesto, no puede haber tales valores.

For erase(it) (donde it es una std::map::iterator), la norma dice que el elemento apuntado por el que se retira - por desgracia, si es end() no apunta a un elemento válido y que están fuera de comportamiento indefinido tierra , como lo sería si usara end() para cualquier otra operación de mapa. Ver la sección 23.1.2 para más detalles.

+3

Para aclarar: hay diferentes sobrecargas de erase(), y la versión del iterador requiere un elemento válido. – rlbond

+12

borrar (it) es equivalente a borrar (it, ++ iterator (it)), lo que me ayuda a ver que el borrar (it) no es válido con it = map.end(). Necesitarías otro iterador después de .end(). –

+0

¿Alguien puede proporcionar un enlace al estándar? –

18

end() no es un interator en el mapa. Es efectivamente 'pasado el final' del mapa.

La versión 'iterador' quiere un iterador para algo en el mapa.
La versión 'clave' de erase hace la búsqueda y se protege contra la clave no encontrada, la versión del iterador asume que no está tratando de romper cosas.

+0

"asume que no estás tratando de romper cosas" ... Entiendo, esperaba borrarlo (haría) una simple comprobación de que! = End() –

+0

No es una idea irrazonable, es solo que muchas partes de los contenedores STL no quieren la sobrecarga de dichos controles. En casos como el borrado, es probable que el iterador haya sido utilizado para otra cosa antes de que quisieras borrar la entrada, por lo que dejan de lado el cheque, porque probablemente ya lo hiciste. En el caso clave, es más probable que la persona que llama no sepa si la clave está en el mapa o no, por lo que hace la verificación. –

+0

Además del comentario anterior, solo para una mejor comprensión del mapa: es un árbol de búsqueda binario (el estándar requiere que se ordene, más comúnmente implementado como árbol rojo-negro), y para borrar una clave, primero hay que encontrarlo de todos modos, ya sea que exista o no, entonces uno está colgando el árbol (haciendo que borrar (clave) sea una operación O (log n)) de todos modos, y aceptar una clave inexistente no implica ningún trabajo adicional (como haría el cheque en erase (it)), por lo que es lógico aceptarlo como entrada. – Aconcagua

1

Aquí hay un ejemplo rápido de cómo uso el mapa STL con los iteradores al eliminar. También hago lo mismo cuando realizo una inserción. Personalmente, me gusta usar typedef para definir específicamente el mapa, pero la elección es suya.


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

Hope this helps!

3

En lugar del ejemplo dado en un post anterior ...

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

la que hace recorridos de dos árboles, uso ...

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

De esa manera hacer un recorrido de árbol y tiene el iterador listo para rodar para otras cosas.