STL удалить не работает, как ожидалось? - PullRequest
13 голосов
/ 23 июня 2011
int main()
{

        const int SIZE = 10;
        int a[SIZE] = {10, 2, 35, 5, 10, 26, 67, 2, 5, 10};
        std::ostream_iterator< int > output(cout, " ");
        std::vector< int > v(a, a + SIZE);
        std::vector< int >::iterator newLastElement;

        cout << "contents of the vector: ";
        std::copy(v.begin(), v.end(), output);

        newLastElement = std::remove(v.begin(), v.end(), 10);
        cout << "\ncontents of the vector after remove: ";
        //std::copy(v.begin(), newLastElement, output); 
                         //this gives the correct result : 2 35 5 26 67 2 5
        std::copy(v.begin(), v.end(), output);
          //this gives a 10 which was supposed to be removed : 2 35 5 26 67 2 5 2 5 10

        cout << endl;
        return 0;
}

В массиве есть три 10.

почему массив v содержит 10 после того, как мы удалили все 10 с помощью функции remove.

вы также можете увидеть скомпилированный вывод здесь

Ответы [ 3 ]

27 голосов
/ 23 июня 2011

На самом деле std::remove не удаляет элемент из контейнера.Цитируется здесь

Удалить удаляет из диапазона [first, last) все элементы, равные value.То есть, remove возвращает итератор new_last такой, что диапазон [first, new_last) не содержит элементов, равных value. Итераторы в диапазоне [new_last, last) все все еще разыменовываются , но элементы, на которые они указывают, являются неопределенными . Удалить является стабильным, означая, что относительный порядокэлементов, которые не равны значению, не изменяется.

То есть std::remove работает только с парой итераторов и ничего не знает о контейнере, который фактически содержит элементы.На самом деле, std::remove не может знать нижележащий контейнер, потому что он никак не может от пары итераторов узнать о контейнере, к которому принадлежат итераторы.Так что std::remove на самом деле не удаляет элементы, просто потому, что не может .Единственный способ * на самом деле удалить элемент из контейнера - вызвать функцию-член для этого контейнера.

Поэтому, если вы хотите удалить элементы, используйте Erase-RemoveИдиома :

 v.erase(std::remove(v.begin(), v.end(), 10), v.end()); 

Идиома erase-remove настолько распространена и полезна, что std::list добавила еще одну функцию-член под названием list::remove, которая производиттот же эффект, что и для идиомы erase-remove.

 std::list<int> l;
 //...
 l.remove(10); //it "actually" removes all elements with value 10!

Это означает, что вам не нужно использовать идиому erase-remove при работе с std::list.Вы можете напрямую вызвать функцию-член list::remove.

11 голосов
/ 23 июня 2011

Причина в том, что алгоритмы STL не изменяют размер последовательности.remove вместо того, чтобы фактически стирать элементы, перемещает их и возвращает итератор в «новый» конец.Затем этот итератор может быть передан функции-члену erase вашего контейнера для фактического выполнения удаления:

v.erase(std::remove(v.begin(), v.end(), 10), v.end());

Кстати, это называется идиомой удаления-удаления.

РЕДАКТИРОВАТЬ: я был не прав.Смотрите комментарии и ответ Наваза.

1 голос
/ 23 июня 2011

Поскольку std::remove на самом деле не сжимает контейнер, он просто перемещает все элементы вниз, чтобы заполнить место, используемое «удаленным» элементом. Например, если у вас есть последовательность 1 2 3 4 5 и вы используете std::remove для удаления значения 2, ваша последовательность будет выглядеть как 1 3 4 5 5. Если вы затем удалите значение 4, вы получите 1 3 5 5 5. Ни в коем случае последовательность не может быть короче.

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