Есть ли причина использовать `remove` вне идиомы erase-remove? - PullRequest
7 голосов
/ 06 октября 2011

Что касается алгоритма, удаление набора элементов из смежного массива может быть эффективно сделано из двух частей.

  1. Переместите все элементы, которые не должны быть удалены, в началоarray.
  2. Отметить массив поменьше.

Это можно сделать в C++ с идиомой удаления-удаления.

vector<T> v; // v = {0,1,2,3,0,0,7};
vector<T>::iterator it = remove(v.begin(),v.end(),e);
// move all elements not to be deleted to the front
// Yes, remove is not the brightest name for that.
// Especially as list::remove really remove elements from the list.
// now v = {1,2,3,7,?,?,?} results marked in question marks
// are implementation dependent.
v.erase(it,v.end());
// get rid of the elements marked as question marks.
// v = {1,2,3,7}

Теперь содержимоеэлементов в вопросительном знаке неизвестно.Единственное, что мы можем с ними сделать, это избавиться от них (перезаписав их или удалив).

Есть ли в реальной ситуации ситуация, когда вам нужно использовать remove без стирания?Единственная ситуация, о которой я могу подумать, это

copy(src.begin(),src.end(),remove(v.begin(),v.end(),e),v.end());

, заменив все A с B с, и требуя, чтобы все эти новые B были смежными.Не имеет особого смысла.

edit : Имеет ли это смысл для чего-либо кроме контейнера памяти (на самом деле deque и vector)?

Если я действительно прав, почему он был реализован как отдельный алгоритм, а не vector::remove_if, dequeue::remove_if и т. Д.

Ответы [ 5 ]

6 голосов
/ 06 октября 2011

Ваш вопрос идет не так, как надо.Скорее, можно было бы спросить: «Почему я должен повторять этот идентичный алгоритм для каждого контейнера, а не делать его единственной свободной функцией?» *

. Ключевая идея разделения контейнеров, итераторов и алгоритмов состоит в том, что вы неСложность возрастает, когда вы пишете больше алгоритмов и больше контейнеров.Если вы хотите написать новый контейнер с непрерывным хранилищем, вы можете использовать remove прямо из коробки (при условии, что вы предоставляете итераторы прямого или произвольного доступа), без дублирования любого кода.

Единственная причина, по которой некоторые контейнеры реализуют свои собственные версии алгоритмов-членов, заключается в том, что они могут делать это лучше, чем общая версия (например, std::set::find против std::find; или list::remove), или потому, чтоони могут делать то, что не может сделать универсальная версия (например, std::list::sort против std::sort).Но если вы можете, вы должны использовать бесплатную версию для максимальной универсальности и универсальности.

4 голосов
/ 06 октября 2011

Прежде всего: фактически реализация remove_if в качестве методов-членов означала бы дублирование кода снова и снова.Я всегда считал, что ошибка была в list (почему remove: MarkB ответил на это, в C ++ 03 это более эффективно, а в C ++ 11 это немного более эффективно)

Это цель разработки STL: разделить структуры данных и алгоритмы так, чтобы при наличии N структур и M алгоритмов у вас не было N*M реализаций алгоритмов, а только N+M, что, безусловно, большеуправляемый.

Примечательно, что спецификация remove, что элементы "пропущены" имеют неопределенное значение, позволяет применять новые операции C ++ 11 move для эффективного удаления.

Теперь я признаю, что в большинстве случаев он используется как часть идиомы удаления-стирания.Никто не мешает вам создать свой собственный:

 template <typename C>
 void erase(C& container, typename C::const_reference r) {
   container.erase(std::remove(container.begin(), container.end(), r), container.end());
 }
1 голос
/ 06 октября 2011

Да. Простой контрпример:

void bar {
  // Get vector of T's from foo()
  vector<T> v = foo();
  // Print only non-zero elements.
  vector<T>::iterator it = remove(v.begin(),v.end(), T(0));
  // I could call erase here, but why bother?
  std::copy(v.begin(), it, std::ostream_iterator<T>(std::cout));
}

Если я собираюсь использовать только выбранную часть вектора, и у меня нет другого использования для вектора, мне не нужно erase вектор. Все элементы будут уничтожены в любом случае, когда вектор выйдет из области видимости.

0 голосов
/ 06 октября 2011

Да. прежде всего remove() не зависит от контейнера. Вы можете использовать его на 2 итераторах любого контейнера и не должны делать такие вещи, как:

if(<iterator is vector iterator>)
{
   vector::remove_if(...);
}
else(<iterator is dequeue iterator>)
{
   dequeue::remove_if(...);
}
else(<iterator is list iterator>)
{
   list::remove_if(...);
}
// etc

Да, есть причина не выполнять erase(). Для вектора это делает его меньше и, следовательно, выполняет перераспределение памяти. Что не всегда желательно, потому что это приведет к множеству конструкторов копирования для других элементов вектора.

0 голосов
/ 06 октября 2011

Если по какой-то причине вы не хотите изменять размер своего контейнера, а просто удалить некоторые элементы, а затем задать другие значения для замены удаленного пространства, тогда вы можете просто использовать команду remove.

Как я понимаю, значение удаленных элементов после удаления не указывается, поэтому вы не можете использовать их данные. Они вроде удалены, но пространство не освобождается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...