c++ - for - std:: remove con vector:: borrar y comportamiento indefinido
vector c++ español (3)
Entonces, ¿es el comportamiento indefinido de vector.erase (vector.end (), vector.end ())?
No. Debido a la afirmación que está justo al lado de la que usted hizo hincapié:
Iteradores que especifican un rango dentro del vector] a eliminar: [primero, último). es decir, el rango incluye todos los elementos entre el primero y el último, incluido el elemento señalado por el primero pero no el señalado por el último .
Entonces, vector.erase(vector.end(),vector.end())
no intenta borrar vector.end()
porque está apuntado por el last
parámetro.
Por supuesto, esta definición es ambigua y esas declaraciones pueden interpretarse como contradictorias. La fraseología citada no es utilizada por la norma.
En toda la web veo que las personas usan la expresión borrar / eliminar para los vectores C ++ de esta manera:
#include <vector> // the general-purpose vector container
#include <iostream>
#include <algorithm> // remove and remove_if
int main()
{
// initialises a vector that holds the numbers from 0-9.
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// removes all elements with the value 5
v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() );
return 0;
}
Es decir, si quiero borrar todos los elementos que coinciden con algunos criterios (por ejemplo, el número 5 de un vector de int
s), entonces uso std::remove
o std::remove_if
junto con vector.erase
así:
vector.erase( std::remove( vector.begin(), vector.end(), <some_value>), vector.end());
Esto funciona bien en general; std::remove
(y remove_if
) copiará (o usará la semántica de movimiento en C ++ 11) los elementos que se van a eliminar hasta el final del vector, por lo que el vector de nuestro ejemplo anterior se verá así:
{0, 1, 2, 3, 4, 6, 7, 8, 9, 5 };
Con el elemento 5 en negrita porque ha sido movido hasta el final.
Ahora, std::remove
le devolverá un iterador, que luego usaremos en erase
para borrar los elementos. Bonito.
Pero, ¿qué pasa con el siguiente ejemplo?
int main()
{
// initialises an empty vector.
std::vector<int> v = {};
// removes all elements with the value 5
v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() );
return 0;
}
Esto parece funcionar como se espera (sin borrar nada, no segfaulting, etc.) en todas las plataformas en las que lo ejecuto, pero sé que solo porque algo funciona, no significa que no sea un comportamiento indefinido.
La reference rápida para vector.erase
dice esto (énfasis mío):
iterator erase (const_iterator first, const_iterator last);
first, last
son
Iteradores que especifican un rango dentro del vector] a eliminar:
[first,last)
. es decir, el rango incluye todos los elementos entre elfirst
y ellast
, incluido el elemento señalado por el primero pero no el señalado por ellast
. Los tipos de miembrosconst_iterator
yconst_iterator
son tipos de iteradores de acceso aleatorio que apuntan a elementos.
Entonces, ¿es el vector.erase(vector.end(),vector.end())
?
Esto es lo que dice la referencia rápida sobre la seguridad de excepciones:
Si los elementos eliminados incluyen el último elemento del contenedor, no se lanzan excepciones (garantía de no-throw). De lo contrario, se garantiza que el contenedor terminará en un estado válido (garantía básica). Una
position
orange
no válido causa un comportamiento indefinido.
Entonces, la respuesta, al menos para mí, parece ser "SÍ", y esta respuesta de StackOverflow parece ser compatible.
Por lo tanto, ¿es incorrecto el idioma común?
Asumiendo que es un comportamiento indefinido, cualquier llamada a remove
podría devolver un iterador a vector.end()
que debería verificarse antes de llamar a vector.erase
, y llamar a eliminar en un vector vacío parece devolver vector.end
: ( IDEOne para el código a continuación) )
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
vector<int> myInts;
auto anIter = std::remove(myInts.begin(),myInts.end(),5);
if (anIter == myInts.end())
std::cout << "iterator = myInts.end()";
}
Finalmente, mi pregunta:
¿Debe el idioma eliminar / borrar real ser esto?
auto endOfRangeIterator = std::remove(vector.begin(), vector.end(), <value>);
if (endOfRangeIterator != vector.end())
vector.erase(endOfRangeIterator, vector.end())
24.2.1 / 7 La mayoría de las plantillas algorítmicas de la biblioteca que operan en estructuras de datos tienen interfaces que usan rangos. Un rango es un par de iteradores que designan el comienzo y el final del cálculo. Un rango
[i,i)
es un rango vacío ; en general, un rango[i,j)
refiere a los elementos en la estructura de datos que comienzan con el elemento apuntado pori
y hasta, pero sin incluir, el elemento apuntado porj
.
Énfasis mío
Además, la descripción de erase
usted cita no es el texto normativo en el estándar. El estándar tiene esto que decir (Tabla 100):
a.erase(q1,q2)
Efectos : borra los elementos en el rango [q1, q2).
Esto no requiere que q1
sea desreferenciable. Si [q1, q2) es un rango vacío (por 24.2.1 / 7), entonces no hay elementos en el rango, por lo que ninguno se borrará.
Creo que lo más importante en tu cita es:
Iteradores que especifican un rango dentro del vector] a eliminar: [primero, último). es decir, el rango incluye todos los elementos entre el primero y el último, incluido el elemento señalado por el primero pero no el señalado por el último . Los tipos de miembros iterator y const_iterator son tipos de iteradores de acceso aleatorio que apuntan a elementos.
Como hemos encontrado en los comentarios, esta cita de reference es incorrecta. Esto no violará las reglas en caso de ( v.end, v.end)
pero será incorrecto en caso de
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3 };
v.erase( v.begin(), v.begin());
}
porque afirmación que se contradice con
el rango incluye (...), incluido el elemento apuntado por v.begin () pero no el apuntado por v.begin ().
no puede ser una declaración válida.
Norma n3337 de C ++ en § 23.2.2 Requisitos de contenedores de secuencia La Tabla 100 especifica que
a.erase(q1,q2)
devuelve el iterator
. Y nota es:
Requiere: para vector y deque, T será MoveAssignable. Efectos: borra los elementos en el rango [q1, q2) .
Y esto es lo que dice sobre el rango [i,j)
en el § 24.2.1 / 7 Requisitos del iterador
La mayoría de las plantillas algorítmicas de la biblioteca que operan en estructuras de datos tienen interfaces que usan rangos. Un rango es un par de iteradores que designan el comienzo y el final del cálculo. Un rango [i, i) es un rango vacío; en general, un rango [i, j) se refiere a los elementos en la estructura de datos que comienzan con el elemento apuntado por i y hasta, pero sin incluir, el elemento apuntado por j . El rango [i, j) es válido si y solo si j es alcanzable desde i. El resultado de la aplicación de funciones en la biblioteca a rangos no válidos no está definido.
Por lo tanto para responder a sus preguntas
Pero, ¿qué pasa con el siguiente ejemplo?
cplusplus.com está equivocado en este caso
Entonces, ¿es el comportamiento indefinido de vector.erase (vector.end (), vector.end ())?
No, no se desencadena un comportamiento indefinido.
Por lo tanto, ¿es incorrecto el idioma común?
No, es correcto
¿Debe el idioma eliminar / borrar real ser esto?
No hay necesidad de esto, aunque también está bien.