Ответы:
Если в векторе есть хотя бы 3 элемента, удалить последние 3 элемента просто - просто используйте pop_back 3 раза:
#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';
}
Вывод:
1 2
Это неопределенное поведение , чтобы передать end()
итератор на 1-параметра erase()
перегрузки. Даже если это не так, erase()
аннулирует итераторы, которые находятся «в и после» указанного элемента, делая d
недействительным после 1-й итерации цикла.
std::vector
имеет erase()
перегрузку с двумя параметрами, которая принимает диапазон элементов для удаления. Вам не нужен ручной цикл вообще:
if (X.size() >= 3)
X.erase(X.end()-3, X.end());
Во-первых, X.end()
не возвращает итератор к последнему элементу вектора, он скорее возвращает итератор к элементу после последнего элемента вектора, который является элементом, которым вектор фактически не владеет, поэтому, когда вы пытаетесь стереть с X.erase(d)
помощью программы вылетает.
Вместо этого, при условии, что вектор содержит как минимум 3 элемента, вы можете сделать следующее:
X.erase( X.end() - 3, X.end() );
Который вместо этого идет к третьему последнему элементу и стирает каждый элемент после этого, пока не доберется до X.end()
.
РЕДАКТИРОВАТЬ: просто чтобы уточнить, X.end()
это LegacyRandomAccessIterator, который указан, чтобы иметь допустимую -
операцию, которая возвращает другой LegacyRandomAccessIterator .
Определение end()
из cppreference :
Возвращает итератор, ссылающийся на элемент past-the-end в векторном контейнере.
и чуть ниже:
Он не указывает на какой-либо элемент и, следовательно, не должен быть разыменован.
Другими словами, вектор не имеет элемента, на который указывает end (). По разыменованию , что не-элемент через метод стирания (), вы , возможно , изменяя память , которая не принадлежит к вектору. Отсюда могут происходить ужасные вещи.
Это обычное соглашение C ++ для описания интервалов как [low, high), с «низким» значением, включенным в интервал, и «высоким» значением, исключенным из интервала.
Вы можете использовать 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;
}
Есть несколько вещей, чтобы упомянуть:
reverse_iterator rit
начинается с последнего элемента vector X
. Эта позиция называется rbegin
.erase
требует классики iterator
для работы. Мы получаем это от rit
звонка base
. Но этот новый итератор будет указывать на следующий элемент rit
в прямом направлении.rit
до вызова base
иerase
Также, если вы хотите узнать больше о reverse_iterator
, я предлагаю посетить этот ответ .
Комментарий (теперь удаленный) в вопросе гласил, что «для итератора нет оператора». Тем не менее, следующий код компилируется и работает в обоих MSVC
и clang-cl
, со стандартным набором либо C++17
или 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;
}
Ниже приводится определение operator-
(в <vector>
заголовке):
_NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
_Vector_iterator _Tmp = *this;
return _Tmp -= _Off;
}
Тем не менее, я определенно не адвокат по языку C ++, и вполне возможно, что это одно из тех «опасных» расширений Microsoft. Мне было бы очень интересно узнать, работает ли это на других платформах / компиляторах.
-
определены для этих типов итераторов.
operator-
определено для итераторов, вы можете просто использовать std::advance()
или std::prev()
вместо.
Это утверждение
if (i == 1) X.erase(d);
имеет неопределенное поведение.
И это утверждение пытается удалить только элемент перед последним элементом
else X.erase(d - i);
потому что у вас есть цикл только с двумя итерациями
for (size_t i = 1; i < 3; i++) {
Вам нужно что-то вроде следующего.
#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;
}
Выход программы
1 2
d
самом деле не существует. Это значение канарейки «один за другим» можно использовать только для того, чтобы найти конецvector
. Вы не можете удалить это. Далее, как только вы удалите итератор, он исчезнет. Вы не можете безопасно использовать его потом ни для чего, в том числеd - i
.