Where are items deleted by std :: remove_if deleted?

reference says that

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

Iterators pointing to elements between the old and new ends of the range are still wanted, but the elements themselves are vague values.

I tried this simple program to find out what they mean by "unspecified values."

 #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; } 

Output:

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

This is something mysterious happens to the data after the 7th element, which causes segfault.

Interestingly, a possible implementation from here

 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; } 

Does not create segfault.

This is mistake? Because iterators must be unsolvable. I am using gcc 4.7.3

+4
source share
3 answers

First, if you don’t know, you need to remember something very important when you use std::remove and std::remove_if : they cannot actually erase elements from the main container . This means that they themselves do not actually delete anything.

You need to use something like idiom remove / erase:

 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()); 

What happens to erased elements is determined by implementation. Here is the gcc implementation:

  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; } 

Probably what causes segfault is the fact that this implementation calls _GLIBCXX_MOVE .

+8
source

Iterators can be spotted, but there may not be generic pointers. You must check the nullness value before dereferencing a generic pointer with an undefined value.

+7
source

The C ++ 11 compiler will use move semantics, if possible, move elements that are not removed by ' std::remove_if . Moving shared_ptr leaves the original shared_ptr object empty (it no longer owns the pointer) - call get() to the fact that the original shared_ptr returns a null pointer.

Therefore, if you cast on shared_ptr, you will get the dereference of the null pointer.

So, overall, although the iterator is still dereferenced, shared_ptr might not be.

+4
source

Source: https://habr.com/ru/post/1480051/


All Articles