Что делает std :: remove?
Вот псевдокод std::remove
. Потратьте несколько секунд, чтобы увидеть, что происходит, а затем прочитайте объяснение.
Iter remove(Iter start, Iter end, T val) {
Iter destination = start;
//loop through entire list
while(start != end) {
//skip element(s) to be removed
if (*start == val) {
start++;
}
else //retain rest of the elements
*destination++ = *start++;
}
//return the new end of the list
return destination;
}
Обратите внимание, что remove просто перемещает вверх элементы в последовательности, перезаписывая значения, которые вы хотели удалить. Значит, значения, которые вы хотели удалить, действительно исчезли, но тогда в чем проблема? Допустим, у вас был вектор со значениями {1, 2, 3, 4, 5}. После вызова удаления для val = 3 вектор теперь имеет {1, 2, 4, 5, 5}. То есть 4 и 5 сместились вверх, так что 3 ушло от вектора, но размер вектора не изменился. Кроме того, конец вектора теперь содержит дополнительную оставшуюся копию 5.
Что делает vector :: erase?
std::erase
принимает начало и конец диапазона, от которого вы хотите избавиться. значение , которое вы хотите удалить, не принимает только начало и конец диапазона. Вот псевдокод того, как это работает:
erase(Iter first, Iter last)
{
//copy remaining elements from last
while (last != end())
*first++ = *last++;
//truncate vector
resize(first - begin());
}
Таким образом, операция стирания фактически изменяет размер контейнера и освобождает память.
идиома удаления-стирания
Комбинация std::remove
и std::erase
позволяет вам удалить совпадающие элементы из контейнера, так что контейнер будет фактически обрезан, если элементы были удалены. Вот как это сделать:
//first do the remove
auto removed = std::remove(vec.begin(), vec.end(), val);
//now truncate the vector
vec.erase(removed, vec.end());
Это называется идиомой удаления-стирания. Почему это так? Идея заключается в том, что операция поиска элементов является более общей и независимой от базового контейнера (зависит только от итераторов). Однако операция стирания зависит от того, как контейнер хранит память (например, у вас может быть связанный список вместо динамического массива). Таким образом, STL ожидает, что контейнеры будут выполнять свое собственное стирание, обеспечивая при этом общую операцию «удаления», поэтому все контейнеры не должны реализовывать этот код. На мой взгляд, это название вводит в заблуждение, и std::remove
следовало бы назвать std::find_move
.
Примечание: вышеуказанный код является строго псевдокодом. Реальная реализация STL более разумна, например, с использованием std::move
вместо copy.