c++ - ¿Por qué std:: begin y std:: end "no son seguros para la memoria"?
c++11 iterator (6)
En esta publicación del blog , Eric Niebler afirma que:
¿Qué está mal con std :: begin y std :: end? ¡Sorpresa! No son seguros para la memoria. Considera lo que hace este código:
extern std::vector<int> get_data(); auto it = std::begin(get_data()); int i = *it; // BOOM
std :: begin tiene dos sobrecargas para valores const y non-const. El problema es que los valores se unen a las referencias constantes de valores, lo que lleva al iterador que cuelga hacia arriba.
Estoy teniendo problemas para entender su punto y por qué es una referencia que cuelga. ¿Podría alguien explicar?
¿Es justo decir que esto es un fallo de seguridad de memoria de std :: begin? No hay una forma ''segura'' de crear un iterador de tal manera que sea válido seguir el puntero una vez que se elimine el contenedor.
Carece de una verificación que tenga otro método, pero no es como si no hubiera una manera de hacer que un iterador derivado de rangos se reviente. Solo tienes que esforzarte un poco más ...
Creo que el punto de vista de Eric sobre
std::begin
es que
acepta
silenciosamente un contenedor de valores como un argumento para comenzar.
A primera vista, el problema con el código también se ejemplifica en
auto it = get_data().begin();
Pero
std::begin
es una plantilla de función gratuita, se puede hacer para rechazar rvalues sin necesidad de agregar los calificadores de referencia adecuados a los miembros iniciales de cada contenedor.
Al "solo" reenviarlo, se pierde la oportunidad de agregar una capa de seguridad de memoria al código.
Idealmente, el conjunto de sobrecarga podría haberse beneficiado de la adición de
template< class C >
void begin( C&& ) = delete;
Ese hubiera causado que el código en la publicación del blog fuera rechazado de plano en el acto.
El vector temporal devuelto por
get_data
queda fuera del alcance después de que se realiza
std::begin
.
No se mantiene vivo, por
it
es un iterador en un objeto destruido.
La función
get_data
devuelve un objeto.
Cuando se usa de la manera que se muestra, ese objeto será un objeto
temporal
, que se destruirá una vez que finalice la expresión completa.
El iterador ahora hace referencia a un objeto vectorial que ya no existe, y no puede ser desreferido o usado de ninguna manera útil.
Porque permite la inicialización desde un valor de r, lo que es malo.
de hecho, la función complicada se puede simplificar a dos funciones cortas, como
int& foo(int x){
return x;
}
int generate_a_int(){
return 42;
}
y luego invocarlo foo (generate_a_int ()), se genera un valor temporal, una vez que sale del cuerpo de la función, el valor temporal generado por generate_a_int () se destruye, y luego se produce una referencia a la suspensión ...
¿Entiendes ahora?