Respostas:
Se houver pelo menos 3 itens no vetor, excluir os 3 últimos itens é simples - basta usar pop_back 3 vezes:
#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';
}
Resultado:
1 2
É um comportamento indefinido passar o end()iterador para a erase()sobrecarga de 1 parâmetro . Mesmo se não fosse, erase()invalida os iteradores "no e depois" do elemento especificado, tornando dinválido após a primeira iteração do loop.
std::vectorpossui uma erase()sobrecarga de 2 parâmetros que aceita um intervalo de elementos para remover. Você não precisa de um loop manual:
if (X.size() >= 3)
X.erase(X.end()-3, X.end());
Primeiro, X.end()não retorna um iterador para o último elemento do vetor, mas retorna um iterador para o elemento após o último elemento do vetor, que é um elemento que o vetor realmente não possui, é por isso que quando você tenta apague-o com X.erase(d)o programa trava.
Em vez disso, desde que o vetor contenha pelo menos três elementos, você pode fazer o seguinte:
X.erase( X.end() - 3, X.end() );
Que, em vez disso, vai para o terceiro último elemento e apaga todos os elementos depois até chegar a X.end().
EDIT: Apenas para esclarecer, X.end()é um LegacyRandomAccessIterator que está especificado para ter uma -operação válida que retorna outro LegacyRandomAccessIterator .
A definição de end()from cppreference é:
Retorna um iterador referente ao elemento passado no final do contêiner de vetor.
e um pouco abaixo:
Não aponta para nenhum elemento e, portanto, não deve ser desreferenciado.
Em outras palavras, o vetor não possui nenhum elemento para o qual end () aponte. Ao desreferenciar esse não elemento através do método erase (), você possivelmente está alterando a memória que não pertence ao vetor. Daí coisas feias podem acontecer a partir daí.
É a convenção usual do C ++ descrever intervalos como [baixo, alto), com o valor "baixo" incluído no intervalo e o valor "alto" excluído do intervalo.
Você pode usar um 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;
}
Há poucas coisas a serem mencionadas:
reverse_iterator ritcomeça no último elemento do vector X. Esta posição é chamada rbegin.eraserequer clássico iteratorpara trabalhar. Nós conseguimos isso ritligando base. Mas esse novo iterador apontará para o próximo elemento de ritfrente.ritantes de ligar baseeeraseAlém disso, se você quiser saber mais reverse_iterator, sugiro visitar esta resposta .
Um comentário (agora excluído) na pergunta afirmou que "não existe operador para um iterador". No entanto, o código a seguir compila e funciona em ambos MSVCe clang-cl, com o padrão definido como C++17ou 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;
}
A definição fornecida para operator-é a seguinte (no <vector>cabeçalho):
_NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
_Vector_iterator _Tmp = *this;
return _Tmp -= _Off;
}
No entanto, certamente não sou advogado da linguagem C ++ e é possível que essa seja uma daquelas extensões 'perigosas' da Microsoft. Eu ficaria muito interessado em saber se isso funciona em outras plataformas / compiladores.
-são definidos para esses tipos de iteradores.
operator-definido para os iteradores, você pode simplesmente usar std::advance()ou em std::prev()vez disso.
Esta afirmação
if (i == 1) X.erase(d);
tem comportamento indefinido.
E esta declaração tenta remover apenas o elemento antes do último elemento
else X.erase(d - i);
porque você tem um loop com apenas duas iterações
for (size_t i = 1; i < 3; i++) {
Você precisa de algo como o seguinte.
#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;
}
A saída do programa é
1 2
drealmente não existe. É o valor canário de um passado-final que pode ser usado apenas para encontrar o fim dovector. Você não pode removê-lo. Em seguida, assim que você apaga um iterador, ele desaparece. Você não pode usá-lo com segurança posteriormente para qualquer coisa, inclusived - i.