Zabójca tutaj dnaprawdę nie istnieje. Jest to wartość kanonu, która jest jedyną w swoim rodzaju, użyteczną tylko do znalezienia końca vector. Nie możesz tego usunąć. Następnie, gdy tylko usuniesz iterator, zniknie. Nie możesz później bezpiecznie używać go do niczego, w tym d - i.
user4581301
Odpowiedzi:
9
Jeśli w wektorze są co najmniej 3 elementy, usunięcie ostatnich 3 elementów jest proste - wystarczy użyć pop_back 3 razy:
#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(constauto&item : v ) std::cout << item <<' ';
std::cout <<'\n';}
Jest niezdefiniowane zachowanie przekazać end()iterator do 1-parametrów erase()przeciążenia. Nawet jeśli tak nie jest, erase()unieważnia iteratory, które są „na i po” określonym elemencie, tworzącd niepoprawny po pierwszej iteracji w pętli.
std::vectorma erase()przeciążenie 2-parametrowe, które akceptuje zakres elementów do usunięcia. W ogóle nie potrzebujesz ręcznej pętli:
Po pierwsze, X.end()nie zwraca iteratora do ostatniego elementu wektora, a raczej zwraca iterator do elementu za ostatnim elementem wektora, który jest elementem, którego wektor tak naprawdę nie posiada, dlatego, gdy próbujesz skasuj to za pomocąX.erase(d) awariami programu.
Zamiast tego, pod warunkiem, że wektor zawiera co najmniej 3 elementy, możesz wykonać następujące czynności:
X.erase( X.end()-3, X.end());
Który zamiast tego przechodzi do trzeciego ostatniego elementu i usuwa następnie każdy element, aż do niego dojdzie X.end().
EDYCJA: Tylko dla wyjaśnienia, X.end()jest LegacyRandomAccessIterator, który jest określony jako mający prawidłową -operację, która zwraca inny LegacyRandomAccessIterator .
Zwraca iterator odnoszący się do elementu past-the-end w kontenerze wektorowym.
i nieco poniżej:
Nie wskazuje na żaden element, a zatem nie można go odrzucić.
Innymi słowy, wektor nie ma elementu, na który wskazuje end (). Dereferencjując ten element pomocą metody erase (), prawdopodobnie zmieniasz pamięć, która nie należy do wektora. Stąd brzydkie rzeczy mogą się odtąd zdarzyć.
Zwykłą konwencją C ++ jest opisywanie przedziałów jako [niska, wysoka), z wartością „niską” zawartą w przedziale, a „wysoką” wartością wykluczoną z przedziału.
#include<iostream>#include<vector>usingnamespace std;int main(){vector<float> X ={1.1,2.2,3.3,4.4,5.5,6.6};// start the iterator at the last elementvector<float>::reverse_iterator rit = X.rbegin();// repeat 3 timesfor(size_t i =0; i <3; i++){
rit++;
X.erase(rit.base());}// display all elements in vector Xfor(float&e: X)
cout << e <<'\n';return0;}
Jest kilka rzeczy do wspomnienia:
reverse_iterator ritzaczyna się od ostatniego elementu vector X. Ta pozycja nazywa się rbegin.
erasewymaga klasycznego iteratordo pracy. Otrzymujemy to rit, dzwoniąc base. Ale ten nowy iterator wskaże następny element z ritprzodu.
Dlatego wyprzedzamy ritprzed basei dzwonimyerase
Również, jeśli chcesz dowiedzieć się więcej reverse_iterator, sugeruję odwiedzenie tej odpowiedzi .
Komentarz (teraz usunięty) w pytaniu stwierdził, że „operator iteratora nie ma operatora”. Jednakże następujące kompiluje kod i pracuje w obu MSVCi clang-cl, ze standardowego zestawu do jednej C++17lub 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;return0;}
Podana definicja operator-jest następująca (w <vector>nagłówku):
Jednak z pewnością nie jestem prawnikiem w języku C ++ i możliwe, że jest to jedno z tych „niebezpiecznych” rozszerzeń Microsoft. Byłbym bardzo zainteresowany wiedzieć, czy to działa na innych platformach / kompilatorach.
Myślę, że jest poprawny, ponieważ iteratory wektora mają dostęp losowy i -są zdefiniowane dla tego typu iteratorów.
PaulMcKenzie
@PaulMcKenzie Rzeczywiście - analizator statyczny clang (który może być dość rygorystyczny ze standardami) nie dał o tym ostrzeżenia.
Adrian Mole
1
Nawet jeśli nie operator-zdefiniowano iteratorów, możesz po prostu użyć std::advance()lub std::prev()zamiast tego.
Remy Lebeau,
1
To oświadczenie
if(i ==1) X.erase(d);
ma niezdefiniowane zachowanie.
Ta instrukcja próbuje usunąć tylko element przed ostatnim elementem
else X.erase(d - i);
ponieważ masz pętlę z tylko dwiema iteracjami
for(size_t i =1; i <3; i++){
Potrzebujesz czegoś takiego.
#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(constauto&item : v ) std::cout << item <<' ';
std::cout <<'\n';return0;}
d
naprawdę nie istnieje. Jest to wartość kanonu, która jest jedyną w swoim rodzaju, użyteczną tylko do znalezienia końcavector
. Nie możesz tego usunąć. Następnie, gdy tylko usuniesz iterator, zniknie. Nie możesz później bezpiecznie używać go do niczego, w tymd - i
.Odpowiedzi:
Jeśli w wektorze są co najmniej 3 elementy, usunięcie ostatnich 3 elementów jest proste - wystarczy użyć pop_back 3 razy:
Wynik:
źródło
Jest niezdefiniowane zachowanie przekazać
end()
iterator do 1-parametrówerase()
przeciążenia. Nawet jeśli tak nie jest,erase()
unieważnia iteratory, które są „na i po” określonym elemencie, tworzącd
niepoprawny po pierwszej iteracji w pętli.std::vector
maerase()
przeciążenie 2-parametrowe, które akceptuje zakres elementów do usunięcia. W ogóle nie potrzebujesz ręcznej pętli:Demo na żywo
źródło
Po pierwsze,
X.end()
nie zwraca iteratora do ostatniego elementu wektora, a raczej zwraca iterator do elementu za ostatnim elementem wektora, który jest elementem, którego wektor tak naprawdę nie posiada, dlatego, gdy próbujesz skasuj to za pomocąX.erase(d)
awariami programu.Zamiast tego, pod warunkiem, że wektor zawiera co najmniej 3 elementy, możesz wykonać następujące czynności:
Który zamiast tego przechodzi do trzeciego ostatniego elementu i usuwa następnie każdy element, aż do niego dojdzie
X.end()
.EDYCJA: Tylko dla wyjaśnienia,
X.end()
jest LegacyRandomAccessIterator, który jest określony jako mający prawidłową-
operację, która zwraca inny LegacyRandomAccessIterator .źródło
Definicja
end()
z cppreference jest następująca:i nieco poniżej:
Innymi słowy, wektor nie ma elementu, na który wskazuje end (). Dereferencjując ten element pomocą metody erase (), prawdopodobnie zmieniasz pamięć, która nie należy do wektora. Stąd brzydkie rzeczy mogą się odtąd zdarzyć.
Zwykłą konwencją C ++ jest opisywanie przedziałów jako [niska, wysoka), z wartością „niską” zawartą w przedziale, a „wysoką” wartością wykluczoną z przedziału.
źródło
Możesz użyć
reverse_iterator
:Jest kilka rzeczy do wspomnienia:
reverse_iterator rit
zaczyna się od ostatniego elementuvector X
. Ta pozycja nazywa sięrbegin
.erase
wymaga klasycznegoiterator
do pracy. Otrzymujemy torit
, dzwoniącbase
. Ale ten nowy iterator wskaże następny element zrit
przodu.rit
przedbase
i dzwonimyerase
Również, jeśli chcesz dowiedzieć się więcej
reverse_iterator
, sugeruję odwiedzenie tej odpowiedzi .źródło
Komentarz (teraz usunięty) w pytaniu stwierdził, że „operator iteratora nie ma operatora”. Jednakże następujące kompiluje kod i pracuje w obu
MSVC
iclang-cl
, ze standardowego zestawu do jednejC++17
lubC++14
:Podana definicja
operator-
jest następująca (w<vector>
nagłówku):Jednak z pewnością nie jestem prawnikiem w języku C ++ i możliwe, że jest to jedno z tych „niebezpiecznych” rozszerzeń Microsoft. Byłbym bardzo zainteresowany wiedzieć, czy to działa na innych platformach / kompilatorach.
źródło
-
są zdefiniowane dla tego typu iteratorów.operator-
zdefiniowano iteratorów, możesz po prostu użyćstd::advance()
lubstd::prev()
zamiast tego.To oświadczenie
ma niezdefiniowane zachowanie.
Ta instrukcja próbuje usunąć tylko element przed ostatnim elementem
ponieważ masz pętlę z tylko dwiema iteracjami
Potrzebujesz czegoś takiego.
Wyjście programu to
źródło