Search code examples
c++iteratorstdmapc++17const-iterator

In C++17, why do associative containers have an `erase` member function that takes (non-`const`) `iterator`?


See, e.g., http://en.cppreference.com/w/cpp/container/map/erase

In C++03 there were three overloads:

void erase( iterator pos );
void erase( iterator first, iterator last );
size_type erase( const key_type& key );

In C++11, the first and second overloads were changed to take const_iterator so that they could be called with either iterator or const_iterator. The first overload was also improved by having it return the iterator to the element after the one erased:

iterator erase( const_iterator pos );
void erase( const_iterator first, const_iterator last );
size_type erase( const key_type& key );

In C++17, a non-const overload was re-introduced:

iterator erase( const_iterator pos );
iterator erase( iterator pos );
void erase( const_iterator first, const_iterator last );
size_type erase( const key_type& key );

Why is this needed? It was not added for the ranged erase, nor for insert, nor for any of the sequence containers such as vector, deque, and list.


Solution

  • This was done to address LWG defect 2059. Consider the example from the link

    #include <map>
    
    struct X
    {
      template<typename T>
      X(T&) {}
    };
    
    bool operator<(const X&, const X&) { return false; }
    
    void erasor(std::map<X,int>& s, X x)
    {
      std::map<X,int>::iterator it = s.find(x);
      if (it != s.end())
        s.erase(it);
    }
    

    The call to map::erase at the end is ambiguous because both map::erase(const_iterator) and map::erase(key_type const&) are equally good matches as they each require a user defined conversion.

    Reintroducing the map::erase(iterator) overload fixes this problem.