4

I was trying to use remove_if template for a map container but I am getting compiler error for the template argument. I am unable to understand why.

int main()
{
  map<const int, int> intmap;

  intmap[1] = 1;
  intmap[2] = 2;
  intmap[3] = 3;
  intmap[4] = 4;

  auto isOdd = [&](pair<const int, int> it)->bool 
     { return static_cast<bool>(it.second % 2); };

  isOdd(*(intmap.begin()));

 remove_if(intmap.begin(), intmap.end(), isOdd); 
}

This remove_if is throwing compiler errors. Any suggestion to fix it?

Error message is

C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\utility(260) : error C2166: l-value specifies const object
        C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\utility(259) : while compiling class template member function 
        'std::pair<_Ty1,_Ty2> &std::pair<_Ty1,_Ty2>::operator =(std::pair<_Ty1,_Ty2> &&)'
        with
        [
            _Ty1=const int,
            _Ty2=int
        ]
        maperaseif.cpp(29) : see reference to class template instantiation 'std::pair<_Ty1,_Ty2>' being compiled
        with
        [
            _Ty1=const int,
            _Ty2=int
        ]
4

3 回答 3

5

remove_if works by scanning the elements and once an element is to be removed, it remembers the "gap" that will leave (keeping an iterator pointing thereto) while advancing another iterator to find the next element to retain... it then starts copying or moving elements from the latter position to the former until it reaches end().

That doesn't work for map, because you can't overwrite the pair<key,value> elements wholesale: the key values aren't allowed to be modified or the sorted-order invariant the implementation needs could be invalidated.

So, you'll need to abandon remove_if. You could use a normal loop, being careful to save the iterator-to-next-element rather than attempting to advance from a just-erased iterator. Lots of other questions about how to erase elements from a map while iterating, e.g. here....

于 2015-03-12T07:36:34.193 回答
2

This little erase_if templated function should do what you want. (I didn't write it, just picked it up from somewhere - so credit to whoever did!)

  template< typename ContainerT, typename PredicateT >
  void erase_if( ContainerT& items, const PredicateT& predicate ) {
    for( auto it = items.begin(); it != items.end(); ) {
      if( predicate(*it) ) it = items.erase(it);
      else ++it;
    }
  };

In your example, you would use it like this:

erase_if(intmap, isOdd); 
于 2015-03-12T07:39:37.623 回答
1

You cannot use remove_if on map, since its value type is actually std::pair<const Key, Value>, but it you look at requirements of remove_if, you can see, that dereferenced iterator type should be MoveAssignable.

Just write loop, or use boost for example.

于 2015-03-12T07:34:14.427 回答