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:
#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';
}
Wynik:
1 2
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::vector
ma erase()
przeciążenie 2-parametrowe, które akceptuje zakres elementów do usunięcia. W ogóle nie potrzebujesz ręcznej pętli:
if (X.size() >= 3)
X.erase(X.end()-3, X.end());
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 .
Definicja end()
z cppreference jest następująca:
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.
Możesz użyć 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;
}
Jest kilka rzeczy do wspomnienia:
reverse_iterator rit
zaczyna się od ostatniego elementu vector X
. Ta pozycja nazywa się rbegin
.erase
wymaga klasycznego iterator
do pracy. Otrzymujemy to rit
, dzwoniąc base
. Ale ten nowy iterator wskaże następny element z rit
przodu.rit
przed base
i 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 MSVC
i clang-cl
, ze standardowego zestawu do jednej C++17
lub 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;
}
Podana definicja operator-
jest następująca (w <vector>
nagłówku):
_NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
_Vector_iterator _Tmp = *this;
return _Tmp -= _Off;
}
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.
-
są zdefiniowane dla tego typu iteratorów.
operator-
zdefiniowano iteratorów, możesz po prostu użyć std::advance()
lub std::prev()
zamiast tego.
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 ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
return 0;
}
Wyjście programu to
1 2
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
.