Куда идут элементы, удаленные std :: remove_if?

ссылка Говорит, что

template< class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );

Итераторы, указывающие на элементы между старым и новым концами
диапазон все еще разыменовывает, но сами элементы имеют
неопределенные значения.

Я попробовал эту простую программу, чтобы выяснить, что они подразумевают под «неопределенными значениями».

#include <vector>
#include <memory>
#include <iostream>
#include <algorithm>

int main()
{
std::vector< std::shared_ptr<int> > ints;
for (int i = 0; i < 10; ++i)
ints.push_back(std::make_shared<int>(i));
std::remove_if(ints.begin(), ints.end(),
[](const std::shared_ptr<int>& element)
{
return *element % 7 != 0;
});
for (int i = 0; i < 10; ++i)
std::cout << *ints[i] << std::endl;
return 0;
}

Выход:

0
7
2
3
4
5
6
The program has unexpectedly finished.

Это что-то таинственное происходит с данными после 7-го элемента, что вызывает segfault.

Интересно, что возможная реализация от Вот

template<class ForwardIt, class UnaryPredicate>
ForwardIt remove_if(ForwardIt first, ForwardIt last,
UnaryPredicate p)
{
ForwardIt result = first;
for (; first != last; ++first) {
if (!p(*first)) {
*result++ = *first;
}
}
return result;
}

Не производит segfault.

Это ошибка? Поскольку итераторы должны быть разыменованными. Я использую gcc 4.7.3

4

Решение

Во-первых, если вы не знаете, вам нужно помнить что-то очень важное, когда вы используете std::remove а также std::remove_if: Oни не может на самом деле стереть элементы из нижележащего контейнера. Это означает, что они сами на самом деле не Удалить что-нибудь.

Вам нужно использовать что-то вроде удалить / стирания идиома:

auto to_erase = std::remove_if(ints.begin(), ints.end(),
[](const std::shared_ptr<int>& element)
{
return *element % 7 != 0;
});
ints.erase(to_erase, ints.end());

То, что происходит с «стертыми» элементами, определяется реализацией. Здесь gcc реализация:

  template<typename _ForwardIterator, typename _Predicate>
_ForwardIterator
remove_if(_ForwardIterator __first, _ForwardIterator __last,
_Predicate __pred)
{
// concept requirements
__glibcxx_function_requires(_Mutable_ForwardIteratorConcept<
_ForwardIterator>)
__glibcxx_function_requires(_UnaryPredicateConcept<_Predicate,
typename iterator_traits<_ForwardIterator>::value_type>)
__glibcxx_requires_valid_range(__first, __last);

__first = _GLIBCXX_STD_A::find_if(__first, __last, __pred);
if(__first == __last)
return __first;
_ForwardIterator __result = __first;
++__first;
for(; __first != __last; ++__first)
if(!bool(__pred(*__first)))
{
*__result = _GLIBCXX_MOVE(*__first);
++__result;
}
return __result;
}

Вероятно, причиной segfault является тот факт, что эта реализация вызывает _GLIBCXX_MOVE,

8

Другие решения

Итераторы могут быть разыменованными, но общие указатели могут не быть. Вы должны проверить нулевое значение перед разыменованием общего указателя с неопределенным значением.

7

Компилятор C ++ 11 будет использовать семантику перемещения, если это возможно, для перемещения элементов, которые не «удалены» std::remove_if, Перемещение shared_ptr оставляет исходный объект shared_ptr пустым (у него больше нет указателя) — вызов get() на этом оригинальном shared_ptr вернет нулевой указатель.

Поэтому, если вы разыменуете этот shared_ptr, вы получите разыменование нулевого указателя.

Итак, в итоге, хотя итератор все еще разыменовывается, shared_ptr может не быть.

4
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector