Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where do the elements removed by std::remove_if go?

Tags:

c++

gcc

stl

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

like image 946
Martin Drozdik Avatar asked May 10 '13 06:05

Martin Drozdik


People also ask

What does Remove_if return C++?

The remove_if() function returns an iterator that points to past the last element that is not removed.

What library is Remove_if in C++?

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.

What STD remove returns?

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).


1 Answers

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.

like image 103
Yuushi Avatar answered Sep 20 '22 00:09

Yuushi