auto
puede ayudar al rendimiento al evitar conversiones implícitas silenciosas . Un ejemplo que encuentro convincente es el siguiente.
std::map<Key, Val> m;
// ...
for (std::pair<Key, Val> const& item : m) {
// do stuff
}
¿Ves el error? Aquí estamos, pensando que estamos tomando elegantemente cada elemento del mapa por referencia constante y usando la nueva expresión de rango para aclarar nuestra intención, pero en realidad estamos copiando cada elemento. Esto se debe a que std::map<Key, Val>::value_type
es std::pair<const Key, Val>
, no std::pair<Key, Val>
. Por lo tanto, cuando (implícitamente) tenemos:
std::pair<Key, Val> const& item = *iter;
En lugar de tomar una referencia a un objeto existente y dejarlo así, tenemos que hacer una conversión de tipo. Se le permite tomar una referencia constante a un objeto (o temporal) de un tipo diferente siempre que haya una conversión implícita disponible, por ejemplo:
int const& i = 2.0; // perfectly OK
La conversión de tipo es una conversión implícita permitida por la misma razón por la que puede convertir a const Key
a Key
, pero tenemos que construir una temporal del nuevo tipo para permitir eso. Por lo tanto, efectivamente nuestro ciclo hace:
std::pair<Key, Val> __tmp = *iter; // construct a temporary of the correct type
std::pair<Key, Val> const& item = __tmp; // then, take a reference to it
(Por supuesto, en realidad no hay un __tmp
objeto, solo está allí para ilustración, en realidad, el temporal sin nombre está destinado a item
durar toda la vida).
Solo cambiando a:
for (auto const& item : m) {
// do stuff
}
simplemente nos ahorró una tonelada de copias; ahora el tipo de referencia coincide con el tipo de inicializador, por lo que no es necesario realizar una conversión temporal, solo podemos hacer una referencia directa.