The short answer is that Meyers does consider const_iterator
s preferable when using an up-to-date implementation of the STL due to improvements made in the C++11 standard.
Before discussing his reasons for giving the anti-const_iterator
advice in the first place and explaining what changed, though, I need to clear up a misconception. You write:
isn't that the whole point of a const_iterator
, that it does not allow modifying the container?
This is a sensible assumption, but in fact this is not quite the purpose of const_iterator
. As Meyers explains in the third edition of Effective C++ (written, notably, before C++11):
Declaring an iterator
const
is like declaring a pointer const
(i.e., declaring a T* const
pointer): the iterator
isn't allowed to point to something different, but the thing it points to may be modified. If you want an iterator that points to something that can't be modified (i.e., the STL analogue of a const T*
pointer), you want a const_iterator
[.]
In short, const_iterator
doesn't protect against modifying the container, it protects against modifying the contained values. This is why Meyers expects insert
to be compatible with const_iterator
: it doesn't modify any of the elements already present in the container.
erase
is a bit stranger, because it causes a contained element to be destroyed, which is a non-const
operation. But note that the element's destructor is not called via the iterator itself; the iterator is merely the means the API provides to specify the item to be erase
d. Semantically, a const_iterator
should be able to serve this purpose as well as an an iterator
.
Now, as for the advice in Effective STL and its subsequent retraction, I'll paraphrase and quote some of Effective Modern C++ on the matter. In Item 13, "Prefer const_iterator
s to iterator
s", Meyers writes:
...in C++98, const_iterators
had only halfhearted support. It wasn't that easy to create them, and once you had one, the ways you could use it were limited....
...there was no simple way to get a const_iterator
from a non-const
container...
Once you had the const_iterator
s...locations for insertions (and erasures) could be specified only by iterator
s. const_iterator
s weren't acceptable.
He gives an example making extensive use of static_cast
to get around these limitations, but points out,
...the code I've shown might not compile, either, because there's no portable conversion from a const_iterator
to an iterator
, not even with a static_cast
. Even the semantic sledgehammer known as reinterpret_cast
can't do the job.
He summarizes:
...const_iterator
s were so much trouble in C++98, they were rarely worth the bother.
These issues were addressed by the C++11 standard. As alluded to in a comment on your question, this standard introduced cbegin
and cend
, which return const_iterator
regardless of whether the container itself is const
. Also, insert
and erase
were given overloads taking const_iterator
. This makes const_iterator
much easier to use.