Risposte:
Se ci sono almeno 3 elementi nel vettore, eliminare gli ultimi 3 elementi è semplice: basta usare pop_back 3 volte:
#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';
}
Produzione:
1 2
È un comportamento indefinito passare l' end()
iteratore al erase()
sovraccarico a 1 parametro . Anche se non lo fosse, erase()
invalida gli iteratori che sono "in e dopo" l'elemento specificato, rendendo d
non valido dopo l'iterazione del 1 ° ciclo.
std::vector
ha un erase()
sovraccarico di 2 parametri che accetta un intervallo di elementi da rimuovere. Non è necessario un loop manuale:
if (X.size() >= 3)
X.erase(X.end()-3, X.end());
Innanzitutto, X.end()
non restituisce un iteratore all'ultimo elemento del vettore, ma restituisce un iteratore all'elemento oltre l'ultimo elemento del vettore, che è un elemento che il vettore non possiede in realtà, ecco perché quando provi a cancellalo con X.erase(d)
il programma si blocca.
Invece, a condizione che il vettore contenga almeno 3 elementi, è possibile effettuare le seguenti operazioni:
X.erase( X.end() - 3, X.end() );
Che invece va al terzo ultimo elemento e cancella ogni elemento successivo fino a quando non arriva a X.end()
.
EDIT: solo per chiarire, X.end()
è un LegacyRandomAccessIterator che è specificato per avere un'operazione valida -
che restituisce un altro LegacyRandomAccessIterator .
La definizione di end()
from cppreference è:
Restituisce un iteratore che fa riferimento all'elemento passato-fine nel contenitore vettoriale.
e leggermente sotto:
Non indica alcun elemento e pertanto non deve essere sottovalutato.
In altre parole, il vettore non ha elementi a cui punta end (). Dereferenziando quel non-elemento attraverso il metodo erase (), è possibile che si stia modificando la memoria che non appartiene al vettore. Da qui possono succedere brutte cose.
È la solita convenzione C ++ per descrivere gli intervalli come [basso, alto), con il valore "basso" incluso nell'intervallo e il valore "alto" escluso dall'intervallo.
Puoi usare 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;
}
Ci sono alcune cose da menzionare:
reverse_iterator rit
inizia dall'ultimo elemento di vector X
. Questa posizione è chiamata rbegin
.erase
richiede il classico iterator
con cui lavorare. Lo capiamo rit
chiamando base
. Ma quel nuovo iteratore punterà all'elemento successivo da rit
avanti.rit
prima di chiamare base
eerase
Inoltre, se vuoi saperne di più reverse_iterator
, ti suggerisco di visitare questa risposta .
Un commento (ora eliminato) nella domanda affermava che "non esiste un operatore per un iteratore". Tuttavia, il codice seguente viene compilato e funziona in entrambi MSVC
e clang-cl
, con lo standard impostato su 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 definizione fornita per il operator-
è la seguente ( <vector>
nell'intestazione):
_NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
_Vector_iterator _Tmp = *this;
return _Tmp -= _Off;
}
Tuttavia, non sono certamente un avvocato del linguaggio C ++ ed è possibile che questa sia una di quelle "pericolose" estensioni di Microsoft. Sarei molto interessato a sapere se funziona su altre piattaforme / compilatori.
-
è definito per quei tipi di iteratori.
operator-
definiti gli iteratori, è possibile semplicemente utilizzare std::advance()
o std::prev()
invece.
Questa dichiarazione
if (i == 1) X.erase(d);
ha un comportamento indefinito.
E questa affermazione tenta di rimuovere solo l'elemento prima dell'ultimo elemento
else X.erase(d - i);
perché hai un ciclo con solo due iterazioni
for (size_t i = 1; i < 3; i++) {
Hai bisogno di qualcosa di simile al seguente.
#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;
}
L'output del programma è
1 2
d
non esiste davvero. È il valore canarino un-passato-la-fine utilizzabile solo per trovare la fine del filevector
. Non puoi rimuoverlo. Successivamente, non appena si cancella un iteratore, non c'è più. Non puoi usarlo in sicurezza in seguito per qualsiasi cosa, inclusod - i
.