ссылка Говорит, что
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
Во-первых, если вы не знаете, вам нужно помнить что-то очень важное, когда вы используете 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
,
Итераторы могут быть разыменованными, но общие указатели могут не быть. Вы должны проверить нулевое значение перед разыменованием общего указателя с неопределенным значением.
Компилятор C ++ 11 будет использовать семантику перемещения, если это возможно, для перемещения элементов, которые не «удалены» std::remove_if
, Перемещение shared_ptr оставляет исходный объект shared_ptr пустым (у него больше нет указателя) — вызов get()
на этом оригинальном shared_ptr вернет нулевой указатель.
Поэтому, если вы разыменуете этот shared_ptr, вы получите разыменование нулевого указателя.
Итак, в итоге, хотя итератор все еще разыменовывается, shared_ptr может не быть.