Во всем Интернете я вижу, что люди используют удалить/удалить идиому для векторов С++, например:
#include <vector> // the general-purpose vector container
#include <iostream>
#include <algorithm> // remove and remove_if
int main()
{
// initialises a vector that holds the numbers from 0-9.
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// removes all elements with the value 5
v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() );
return 0;
}
То есть, если я хочу удалить все элементы, соответствующие некоторым критериям (например, число 5 из вектора int
s), я использую std::remove
или std::remove_if
в сочетании с vector.erase
следующим образом:
vector.erase( std::remove( vector.begin(), vector.end(), <some_value>), vector.end());
Это хорошо работает в целом; std::remove
(и remove_if
) скопирует (или использует семантику перемещения в С++ 11) элементы, которые должны быть удалены до конца вектора, поэтому вектор из нашего предыдущего примера теперь будет выглядеть так:
{0, 1, 2, 3, 4, 6, 7, 8, 9, 5};
С элементом 5 полужирным шрифтом, потому что он был перенесен в конец.
Теперь std::remove
вернет ему итератор, который мы затем используем в erase
, чтобы очистить элементы. Ницца.
Но как насчет следующего примера?
int main()
{
// initialises an empty vector.
std::vector<int> v = {};
// removes all elements with the value 5
v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() );
return 0;
}
Кажется, что это работает как ожидалось (не стирая ничего, а не segfaulting и т.д.) на всех платформах, на которых я запускаю его, но я знаю, что только потому, что что-то работает, это не значит, что это не поведение undefined.
Быстрое reference для vector.erase
говорит об этом (основное внимание):
iterator erase (const_iterator first, const_iterator last);
first, last
являются
Итераторы, определяющие диапазон внутри вектора]:
[first,last)
. то есть диапазон включает в себя все элементы междуfirst
иlast
, , включая элемент, указанный первым, но не тот, на который указываетlast
. Типы участниковiterator
иconst_iterator
являются типами итераторов произвольного доступа, которые указывают на элементы.
Итак, поведение vector.erase(vector.end(),vector.end())
undefined?
Вот что говорится в быстрой ссылке о безопасности исключений:
Если удаленные элементы содержат последний элемент в контейнере, никаких исключений не выбрасывается (гарантия отсутствия броска). В противном случае контейнер, как гарантируется, должен быть закончен в действительном состоянии (основная гарантия). Недопустимый
position
илиrange
вызывает поведение undefined.
Итак, ответ, по крайней мере, мне кажется "ДА", и qaru.site/info/210702/..., похоже, его поддерживает.
Следовательно, является ли распространенная идиома неправильной?
Предполагая, что это поведение undefined, любой вызов remove
мог бы вернуть итератор в vector.end()
, который должен быть проверен перед вызовом vector.erase
, и вызов remove на пустой вектор, кажется, возвращает vector.end
: (IDEOne для кода ниже)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
vector<int> myInts;
auto anIter = std::remove(myInts.begin(),myInts.end(),5);
if (anIter == myInts.end())
std::cout << "iterator = myInts.end()";
}
Наконец, мой вопрос:
Должен ли быть фактический идентификатор удаления/стирания?
auto endOfRangeIterator = std::remove(vector.begin(), vector.end(), <value>);
if (endOfRangeIterator != vector.end())
vector.erase(endOfRangeIterator, vector.end())