Respuestas:
Si hay al menos 3 elementos en el vector, eliminar los últimos 3 elementos es simple: solo use pop_back 3 veces:
#include <vector>
#include <iostream>
int main()
{
std::vector<float> v = { 1, 2, 3, 4, 5 };
for (int i = 0; i < 3 && !v.empty(); ++i)
v.pop_back();
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
}
Salida:
1 2
Es un comportamiento indefinido pasar el end()
iterador a la erase()
sobrecarga de 1 parámetro . Incluso si no fuera así, erase()
invalida los iteradores que están "en y después" del elemento especificado, lo que d
deja de ser válido después de la iteración del primer bucle.
std::vector
tiene una erase()
sobrecarga de 2 parámetros que acepta un rango de elementos para eliminar. No necesita un bucle manual en absoluto:
if (X.size() >= 3)
X.erase(X.end()-3, X.end());
Primero, X.end()
no devuelve un iterador al último elemento del vector, sino que devuelve un iterador al elemento pasado el último elemento del vector, que es un elemento que el vector no posee, por eso cuando intentas borrarlo conX.erase(d)
los bloqueos del programa.
En cambio, siempre que el vector contenga al menos 3 elementos, puede hacer lo siguiente:
X.erase( X.end() - 3, X.end() );
Que en su lugar va al tercer último elemento, y borra cada elemento después de eso hasta que llegue X.end()
.
EDITAR: Solo para aclarar, X.end()
es un LegacyRandomAccessIterator que se especifica que tiene una -
operación válida que devuelve otro LegacyRandomAccessIterator .
La definición de end()
from cppreference es:
Devuelve un iterador que hace referencia al elemento pasado al final en el contenedor del vector.
y ligeramente debajo:
No apunta a ningún elemento y, por lo tanto, no debe ser desreferenciado.
En otras palabras, el vector no tiene ningún elemento al que apunte end (). Al desreferenciar ese no elemento a través del método erase (), posiblemente esté alterando la memoria que no pertenece al vector. Por lo tanto, a partir de ahí pueden pasar cosas feas.
Es la convención habitual de C ++ describir los intervalos como [bajo, alto], con el valor "bajo" incluido en el intervalo y el valor "alto" excluido del intervalo.
Podrías usar un reverse_iterator
:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<float> X = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};
// start the iterator at the last element
vector<float>::reverse_iterator rit = X.rbegin();
// repeat 3 times
for(size_t i = 0; i < 3; i++)
{
rit++;
X.erase(rit.base());
}
// display all elements in vector X
for(float &e: X)
cout << e << '\n';
return 0;
}
Hay pocas cosas para mencionar:
reverse_iterator rit
comienza en el último elemento de la vector X
. Esta posición se llama rbegin
.erase
requiere clásico iterator
para trabajar. Obtenemos eso rit
llamando base
. Pero ese nuevo iterador apuntará al siguiente elemento desde rit
adelante.rit
antes de llamar base
yerase
Además, si desea obtener más información reverse_iterator
, le sugiero que visite esta respuesta .
Un comentario (ahora eliminado) en la pregunta decía que "no hay operador para un iterador". Sin embargo, el siguiente código compila y funciona en ambos MSVC
y clang-cl
, con el estándar establecido en C++17
o C++14
:
#include <iostream>
#include <vector>
int main()
{
std::vector<float> X{ 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f };
for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
std::vector<float>::iterator d = X.end();
X.erase(d - 3, d); // This strongly suggest that there IS a "-" operator for a vector iterator!
for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
return 0;
}
La definición proporcionada para el operator-
es la siguiente (en el <vector>
encabezado):
_NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
_Vector_iterator _Tmp = *this;
return _Tmp -= _Off;
}
Sin embargo, ciertamente no soy un abogado de lenguaje C ++, y es posible que esta sea una de esas extensiones 'peligrosas' de Microsoft. Me interesaría saber si esto funciona en otras plataformas / compiladores.
-
están definidos para esos tipos de iteradores.
operator-
definido para los iteradores, podría usar std::advance()
o en su std::prev()
lugar.
Esta declaración
if (i == 1) X.erase(d);
tiene un comportamiento indefinido
Y esta declaración intenta eliminar solo el elemento anterior al último elemento
else X.erase(d - i);
porque tienes un bucle con solo dos iteraciones
for (size_t i = 1; i < 3; i++) {
Necesitas algo como lo siguiente.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<float> v = { 1, 2, 3, 4, 5 };
auto n = std::min<decltype( v.size() )>( v.size(), 3 );
if ( n ) v.erase( std::prev( std::end( v ), n ), std::end( v ) );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}
La salida del programa es
1 2
d
no existe realmente. Es el valor canario de un pasado que se puede usar solo para encontrar el final de lavector
. No puedes eliminarlo. Luego, tan pronto como borre un iterador, desaparecerá. No puede usarlo con seguridad después para nada, inclusod - i
.