23

Why does calling the erase member function of a container with a const_iterator fail?

It works with a non const iterator.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
SaurabhS
  • 633
  • 1
  • 7
  • 18

4 Answers4

30

This doesn't compile because container::iterator and container::const_iterator are two distinct types and the only (one-argument) version of erase is: iterator erase(iterator);

Not accepting a const_iterator can be viewed as a defect in the language standard: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2350.pdf

There is no particular reason for this restriction. The iterator is only used to indicate a position in the (modifiable) container, and neither in case of insert or erase is the "pointee" of the iterator modified (in case of erase it just conceptually goes out of existence, which is a normal thing to do for const objects).

Current standard indicates a confusion between "iterator constness and container constness" (as do other answers here), and it seems const_iterator might become acceptable for erase in C++0x.


As a workaround, you can validly obtain an iterator from a const_iterator because the container has to be mutable in the first place.

The function below is only compilable for random access iterators, as it might be a bit too slow to do this with other types of iterators.

#include <vector>

template <class Container>
typename Container::iterator to_mutable_iterator(Container& c, typename Container::const_iterator it)
{
    return c.begin() + (it - c.begin());
}

int main()
{
    int arr[] = {1, 5, 2, 5, 3, 4, 5, 1};
    std::vector<int> vec(arr, arr + sizeof(arr) / sizeof(*arr));
    for (std::vector<int>::const_iterator it = vec.begin(); it != vec.end(); ) {
        //if (*it = 5) {  //const_iterator prevents this error
        if (*it == 5) {
            it = vec.erase(to_mutable_iterator(vec, it));
        }
        else {
            ++it;
        }
    }
}

However, it might be better to restructure code so that you don't need a const_iterator in the first place. In this case, it would be better to use the std::remove algorithm. If you need to do more non-mutating work before erasing, you can extract that into a separate method etc.

UncleBens
  • 40,819
  • 6
  • 57
  • 90
6

I just want to emphasize on the general correctness of the answers/comments posted by UncleBens, David Rodriguez, and Ise Westeria.

Regardless of the behaviors of the current (or previous) pre-C++11 compilers, the const correctness of const_iterator (should) stops immediately at that it semantically equals to const T* (or T* const) - note that T itself might be a const type of its own! - so it effectively prevents code modifying the referenced object in the container.

However, as it is perfectly legal to 'delete' a const pointer in C++ (try it, it works!), it should be (and the behavior has been corrected in C++11) legal as well to 'erase" a const iterator from a container, provided the container itself is not const.

It seems Visual Studio 2010 is already behaving correctly by having 'erase' accepting const_iterator, which of course caused me some headaches to track down some other bug, which led me to this post, which eventually clarified the correct behavior of "erase const_iterator" const correctness.

Lin
  • 355
  • 3
  • 10
3

Regarding constness, you can think of a std::container<T>::const_iterator as a const T*.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 2
    Since you were picky with the original question I will be picky with the answer :P, "but I can `delete` a `const T*`". Seriously, I don't think this question adds much value, the fact that access to the contained element is `const` does not necessarily imply that you could not modify the container itself. – David Rodríguez - dribeas Feb 03 '11 at 11:26
  • @David: I just thought I'd mention `const T*` because many beginners seem to think that a `const_iterator` is actually more like a `T* const`. – fredoverflow Feb 03 '11 at 11:37
1

A type const_iterator cannot be used to modify the value of an element or the container.

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • I agree. So there is no way to erase entry with const_iterator? – SaurabhS Feb 03 '11 at 11:45
  • 1
    Yes. That's what `const_iterator` is for. Just given access rights but no modification rights. – Mahesh Feb 03 '11 at 12:37
  • 5
    Interestingly at least in N3092 for C++0x, containers' erase and insert methods indeed take `const_iterator` - there is no particular reason why one shouldn't be good for those methods, except perhaps for the `const_cast` hackery that might be needed to implement them. As long as the container is modifyable, I'm not modifying the referenced item, I'm *only* invalidating the iterator (which happens with `const_iterator` all the time). – UncleBens Feb 03 '11 at 21:01
  • 2
    Deletion is not the same as modification in C++. – Brice M. Dempsey Oct 02 '14 at 14:03