vector<int> v;
v.push_back(1);
v.push_back(v[0]);
Si el segundo push_back provoca una reasignación, la referencia al primer entero en el vector ya no será válida. ¿Entonces esto no es seguro?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
Esto lo hace seguro?
push_back
. Otro póster notó un error en él , que no manejó adecuadamente el caso que usted describe. Nadie más, por lo que puedo decir, argumentó que esto no era un error. No digo que sea una prueba concluyente, solo una observación.Respuestas:
Parece que http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 abordó este problema (o algo muy similar) como un defecto potencial en el estándar:
La resolución propuesta fue que esto no era un defecto:
fuente
v.insert(v.begin(), v[2]);
no puede desencadenar una reasignación. Entonces, ¿cómo responde esto a la pregunta?Sí, es seguro, y las implementaciones de bibliotecas estándar saltan los aros para que así sea.
Creo que los implementadores rastrean este requisito hasta 23.2 / 11 de alguna manera, pero no puedo entender cómo, y tampoco puedo encontrar algo más concreto. Lo mejor que puedo encontrar es este artículo:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
La inspección de las implementaciones de libc ++ y libstdc ++ muestra que también son seguras.
fuente
vec.insert(vec.end(), vec.begin(), vec.end());
?vector.push_back
NO especifica lo contrario. "Provoca la reasignación si el nuevo tamaño es mayor que la capacidad anterior". y (enreserve
) "La reasignación invalida todas las referencias, punteros e iteradores que hacen referencia a los elementos en la secuencia".El estándar garantiza que incluso su primer ejemplo sea seguro. Citando C ++ 11
[secuencia.reqmts]
Entonces, aunque no es exactamente trivial, la implementación debe garantizar que no invalidará la referencia al hacer el
push_back
.fuente
t
, la única pregunta es si antes o después de hacer la copia. Tu última oración es ciertamente incorrecta.t
cumple con las condiciones previas enumeradas, el comportamiento descrito está garantizado. No se permite que una implementación invalide una condición previa y luego la use como una excusa para no comportarse como se especifica.for_each
no invalide los iteradores. No puedo encontrar una referencia parafor_each
, pero veo en algunos algoritmos texto como "op y binary_op no invalidará iteradores o subranges".No es obvio que el primer ejemplo es seguro, porque la implementación más simple de
push_back
sería reasignar primero el vector, si es necesario, y luego copiar la referencia.Pero al menos parece seguro con Visual Studio 2010. Su implementación
push_back
hace un manejo especial del caso cuando empujas un elemento en el vector. El código está estructurado de la siguiente manera:fuente
Esto no es una garantía del estándar, pero como otro punto de datos,
v.push_back(v[0])
es seguro para libc ++ de LLVM .std::vector::push_back
Llamadas de libc ++__push_back_slow_path
cuando necesita reasignar memoria:fuente
__swap_out_circular_buffer
, en cuyo caso esta implementación es realmente segura.__swap_out_circular_buffer
. (He agregado algunos comentarios para tener en cuenta eso.)La primera versión definitivamente NO es segura:
de la sección 17.6.5.9
Tenga en cuenta que esta es la sección sobre carreras de datos, en la que las personas normalmente piensan junto con el enhebrado ... pero la definición real implica relaciones "pasa antes", y no veo ninguna relación de orden entre los múltiples efectos secundarios de
push_back
in jugar aquí, es decir, la invalidación de referencia parece no definirse como ordenada con respecto a la construcción de copia del nuevo elemento de cola.fuente
v[0]
no es un iterador, del mismo modo,push_back()
no toma un iterador. Entonces, desde la perspectiva de un abogado de idiomas, su argumento es nulo. Lo siento. Sé que la mayoría de los iteradores son punteros, y el punto de invalidar un iterador es prácticamente el mismo que para las referencias, pero la parte del estándar que usted cita, es irrelevante para la situación en cuestión.x.push_back(x[0])
es SEGURO.Es completamente seguro
En tu segundo ejemplo tienes
lo cual no es necesario porque si el vector sale de su tamaño, implicará el
reserve
.Vector es responsable de estas cosas, no tú.
fuente
Ambos son seguros ya que push_back copiará el valor, no la referencia. Si está almacenando punteros, eso todavía es seguro en lo que respecta al vector, pero solo sepa que tendrá dos elementos de su vector apuntando a los mismos datos.
Por lo tanto, las implementaciones de push_back deben garantizar que se inserte una copia de
v[0]
. Por contraejemplo, suponiendo una implementación que se reasignaría antes de copiar, seguramente no agregaría una copiav[0]
y, como tal, violaría las especificaciones.fuente
push_back
sin embargo, también cambiará el tamaño del vector, y en una implementación ingenua esto invalidará la referencia antes de que se realice la copia. Entonces, a menos que pueda respaldar esto con una cita del estándar, lo consideraré incorrecto.push_back
copiará el valor en el vector; pero (por lo que puedo ver) eso podría suceder después de la reasignación, en cuyo punto la referencia desde la que intenta copiar ya no es válida.push_back
recibe su argumento por referencia .Desde 23.3.6.5/1:
Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
Como estamos insertando al final, no se invalidarán las referencias si el vector no cambia de tamaño. Entonces, si el vector está
capacity() > size()
garantizado para funcionar, de lo contrario se garantiza que será un comportamiento indefinido.fuente
references
parte de la cita.push_back
).