简短的回答是,由于 C++11 标准的改进,Meyers确实认为const_iterators 在使用 STL 的最新实现时更可取。
不过,在讨论他提出反对const_iterator 建议的原因并解释发生了什么变化之前,我需要澄清一个误解。你写:
这不是const_iterator 的全部意义,它不允许修改容器吗?
这是一个合理的假设,但实际上这并不是const_iterator 的目的。正如 Meyers 在第三版 Effective C++ 中解释的那样(尤其是在C++11 之前编写):
声明iterator const 就像声明一个指针const(即声明一个T* const 指针):iterator 不允许指向不同的东西,但它指向的东西可以修改。如果您想要一个指向无法修改的东西的迭代器(即,const T* 指针的 STL 类似物),您需要一个 const_iterator[.]
简而言之,const_iterator 不能防止修改容器,它可以防止修改包含的值。这就是 Meyers 期望 insert 与 const_iterator 兼容的原因:它不会修改容器中已经存在的任何元素。
erase 有点陌生,因为它会导致一个包含的元素被销毁,这是一个非const 操作。但请注意,元素的析构函数不是通过迭代器本身调用的;迭代器只是 API 提供的用于指定项目为erased 的方法。从语义上讲,const_iterator 和 iterator 应该能够达到此目的。
现在,关于Effective STL 中的建议及其随后的撤回,我将转述并引用一些Effective Modern C++ 中的内容。在第 13 项“首选const_iterators 而非iterators”中,Meyers 写道:
...在 C++98 中,const_iterators 只是半心半意的支持。创建它们并不是那么容易,而且一旦你有了它,你可以使用它的方式就很有限......
...没有简单的方法可以从非const 容器中获取const_iterator...
一旦有了const_iterators...插入(和擦除)的位置,就只能由iterators 指定。 const_iterators 是不可接受的。
他举了一个广泛使用static_cast 来绕过这些限制的例子,但他指出,
...我展示的代码也可能无法编译,因为没有从const_iterator 到iterator 的可移植转换,甚至没有static_cast。即使是被称为reinterpret_cast 的语义大锤也无法完成这项工作。
他总结道:
...const_iterators 在 C++98 中太麻烦了,几乎不值得费心。
C++11 标准解决了这些问题。正如在对您的问题的评论中提到的那样,该标准引入了cbegin 和cend,无论容器本身是否为const,它们都会返回const_iterator。此外,insert 和erase 被赋予了const_iterator 的重载。这使得const_iterator 更易于使用。