The reference says that
template< class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );
Iterators pointing to an elements between the old and the new ends of the range are still dereferenceable, but the elements themselves have unspecified 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;
}
The output is:
0
7
2
3
4
5
6
The program has unexpectedly finished.
That is something mysterious happens to the data after the 7th element, which causes a segfault.
Interestingly, the 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 produce the segfault.
Is this a bug? Since the iterators should be dereferencable. I am using gcc 4.7.3
The remove_if() function returns an iterator that points to past the last element that is not removed.
C++ Forward_list Library - remove_if() Function The C++ function std::forward_list::remove_if() removes elements from the forward_list that fulfills the condition. It removes all elements for which predicate returns true.
std :: remove Transforms the range [first,last) into a range with all the elements that compare equal to val removed, and returns an iterator to the new end of that range. The function cannot alter the properties of the object containing the range of elements (i.e., it cannot alter the size of an array or a container).
Firstly, in case you aren't aware, you need to remember something very important when you use std::remove
and std::remove_if
: they cannot actually erase elements from the underlying container. This means that they themselves don't actually remove anything.
You need to use something like the remove/erase idiom:
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 implementation defined. 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;
}
Likely what is causing the segfault is the fact that this implementation calls _GLIBCXX_MOVE
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With