Hace un tiempo, tuve una discusión con un colega sobre cómo insertar valores en los mapas STL . Preferí
map[key] = value;
porque se siente natural y es claro de leer, mientras que él prefirió
map.insert(std::make_pair(key, value))
Simplemente le pregunté y ninguno de nosotros puede recordar la razón por la cual insertar es mejor, pero estoy seguro de que no fue solo una preferencia de estilo, sino que hubo una razón técnica como la eficiencia. La referencia de SGI STL simplemente dice "Estrictamente hablando, esta función miembro es innecesaria: existe solo por conveniencia".
¿Alguien puede decirme esa razón, o solo estoy soñando que hay una?
Respuestas:
Cuando escribes
no hay forma de saber si reemplazaste el
value
forkey
o si creaste uno nuevokey
convalue
.map::insert()
solo creará:Para la mayoría de mis aplicaciones, generalmente no me importa si estoy creando o reemplazando, por lo que utilizo el más fácil de leer
map[key] = value
.fuente
(res.first)->second
lugar devalue
también en el segundo caso.else
porque creo que usarvalue
es más claro que el iterador. Solo si el tipo del valor tuviera una copia inusual u op == sería diferente, y ese tipo causaría otros problemas al usar contenedores STL como map.map.insert(std::make_pair(key,value))
debería sermap.insert(MyMap::value_type(key,value))
. El tipo devuelto desdemake_pair
no coincide con el tipo tomadoinsert
y la solución actual requiere una conversiónoperator[]
, simplemente compare el tamaño antes y después. Imho poder llamarmap::operator[]
solo para tipos construibles predeterminados es mucho más importante.Los dos tienen una semántica diferente cuando se trata de la clave que ya existe en el mapa. Entonces no son realmente directamente comparables.
Pero la versión del operador [] requiere la construcción predeterminada del valor y luego la asignación, por lo que si esto es más costoso que copiar la construcción, entonces será más costoso. A veces, la construcción predeterminada no tiene sentido, y entonces sería imposible usar la versión del operador [].
fuente
Otra cosa a tener en cuenta
std::map
:myMap[nonExistingKey];
creará una nueva entrada en el mapa, clave paranonExistingKey
inicializada a un valor predeterminado.Esto me asustó muchísimo la primera vez que lo vi (mientras me golpeaba la cabeza contra un desagradable error heredado). No lo hubiera esperado. Para mí, eso parece una operación de obtención, y no esperaba el "efecto secundario". Prefiere
map.find()
cuando salgas de tu mapa.fuente
Si el éxito del constructor predeterminado no es un problema, por favor, por amor de Dios, ve con la versión más legible.
:)
fuente
insert
es mejor desde el punto de excepción la seguridad.La expresión
map[key] = value
es en realidad dos operaciones:map[key]
- Crear un elemento de mapa con valor predeterminado.= value
- copiando el valor en ese elemento.Puede ocurrir una excepción en el segundo paso. Como resultado, la operación se realizará solo parcialmente (se agregó un nuevo elemento al mapa, pero ese elemento no se inicializó con
value
). La situación cuando una operación no está completa, pero se modifica el estado del sistema, se llama operación con "efecto secundario".insert
La operación ofrece una garantía sólida, lo que significa que no tiene efectos secundarios ( https://en.wikipedia.org/wiki/Exception_safety ).insert
está completamente hecho o deja el mapa en estado no modificado.http://www.cplusplus.com/reference/map/map/insert/ :
fuente
Si su aplicación es crítica para la velocidad, aconsejaré usar el operador [] porque crea un total de 3 copias del objeto original, de las cuales 2 son objetos temporales y tarde o temprano se destruyen como.
Pero en insert (), se crean 4 copias del objeto original de las cuales 3 son objetos temporales (no necesariamente "temporales") y se destruyen.
Lo que significa tiempo extra para: 1. Una asignación de memoria de objetos 2. Una llamada de constructor adicional 3. Una llamada de destructor adicional 4. Una desasignación de memoria de objetos
Si sus objetos son grandes, los constructores son típicos, los destructores liberan muchos recursos, los puntos anteriores cuentan aún más. En cuanto a la legibilidad, creo que ambos son lo suficientemente justos.
La misma pregunta vino a mi mente pero no sobre la legibilidad sino la velocidad. Aquí hay un código de muestra a través del cual llegué a conocer el punto que mencioné.
fuente
insert
tiene que hacer la misma búsqueda, por lo que no hay diferencia en eso desde[]
(porque las teclas del mapa son únicas).Ahora en c ++ 11 creo que la mejor manera de insertar un par en un mapa STL es:
El resultado será un par con:
Primer elemento (resultado.primero), apunta al par insertado o apunta al par con esta clave si la clave ya existe.
Segundo elemento (resultado.segundo), verdadero si la inserción fue correcta o falsa, algo salió mal.
PD: Si no tiene dudas sobre el orden, puede usar std :: unordered_map;)
¡Gracias!
fuente
Un problema con map :: insert () es que no reemplazará un valor si la clave ya existe en el mapa. He visto código C ++ escrito por programadores de Java donde esperaban que insert () se comportara de la misma manera que Map.put () en Java, donde se reemplazan los valores.
fuente
Una nota es que también puedes usar Boost.Assign :
fuente
Aquí hay otro ejemplo, que muestra que
operator[]
sobrescribe el valor de la clave si existe, pero.insert
no sobrescribe el valor si existe.fuente
Este es un caso bastante restringido, pero a juzgar por los comentarios que he recibido, creo que vale la pena señalarlo.
He visto personas en el pasado usar mapas en forma de
para evadir casos de sobreescritura accidental de valores, pero luego seguir escribiendo otros bits de código:
Recuerdo que su razón para hacerlo fue porque estaban seguros de que en estos ciertos bits de código no iban a sobrescribir los valores del mapa; por lo tanto, seguir adelante con el método más 'legible'
[]
.En realidad, nunca he tenido ningún problema directo con el código que escribieron estas personas, pero hasta ahora creo firmemente que los riesgos, por pequeños que sean, no deberían tomarse cuando pueden evitarse fácilmente.
En los casos en que se trate de valores de mapas que no se deben sobrescribir, use
insert
. No haga excepciones simplemente por legibilidad.fuente
insert
(noinput
), ya queconst_cast
hará que se sobrescriba cualquier valor anterior, lo cual es muy no constante. O no marque el tipo de valor comoconst
. (Ese tipo de cosas suele ser el resultado final deconst_cast
, por lo que es casi siempre una señal de alerta que indica un error en otro lugar.)insert
en casos en los que desee evitar que se sobrescriban los valores. (Acabo de cambiar elinput
ainsert
- gracias)const_cast<T>(map[key])
eran 1. [] es más legible, 2. confían en ciertos bits de código que no sobrescribirán valores, y 3. no lo hacen desea que otros bits de código desconocido sobrescriban sus valores, de ahí elconst value
.const_cast
parece más que negar la "legibilidad" adicional de[]
, y ese tipo de confianza es casi suficiente para despedir a un desarrollador. Las difíciles condiciones de tiempo de ejecución se resuelven con diseños a prueba de balas, no con instintos.El hecho de que la función std :: map
insert()
no sobrescribe el valor asociado con la clave nos permite escribir un código de enumeración de objetos como este:Es un problema bastante común cuando necesitamos asignar diferentes objetos no únicos a algunas identificaciones en el rango 0..N. Esos id se pueden usar más tarde, por ejemplo, en algoritmos de gráficos. La alternativa con
operator[]
parecería menos legible en mi opinión:fuente