v(a, a + SIZE); std::vector< int >::iterator newLastEle" />

STL удалить не работает, как ожидалось?

c++ stl

4822 просмотра

3 ответа

1690 Репутация автора

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.

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

Автор: munish Источник Размещён: 23.06.2011 03:44

Ответы (3)


11 плюса

26933 Репутация автора

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

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

Между прочим, это известно как идиома удаления-удаления.

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

Автор: Etienne de Martel Размещён: 23.06.2011 03:49

1 плюс

993 Репутация автора

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

Автор: Josh Размещён: 23.06.2011 03:49

28 плюса

265725 Репутация автора

Решение

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

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

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

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

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

Стирающий удалить идиомы настолько общее и полезным в том , что 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.

Автор: Nawaz Размещён: 23.06.2011 03:51
Вопросы из категории :
32x32