Ответы:
Если в векторе есть хотя бы 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.