Effective STL 条款27

类别:编程语言 点击:0 评论:0 推荐:
条款27:用distance和advance把const_iterator转化成iterator

条款26中指出有些容器成员函数只接受iterator作为参数,而不是const_iterator。那么,如果你只有一个const_iterator,而你要在它所指向的容器位置上插入新元素呢?也就是如何把const_iterator转化为iterator呢?因为正如条款26所解释的,并不存在从const_iterator到iterator之间的隐式转换,所以你必须成为这次行动的主角。

我知道你在想什么。你正在想,“每当无路可走的时候,就举起大锤!”。在C++的世界里,你的意思只能是:映射(casting)。这种想法很可耻。真不知道你是哪儿学来的。

让我们面对困扰在你面前的问题。看看当你把一个const_iterator映射为iterator时会发生什么:

typedef deque<int> IntDeque; // typedef, 简化代码。 typedef IntDeque::iterator Iter; typedef IntDeque::const_iterator ConstIter; ConstIter ci; // ci是const_iterator ... Iter i(ci); // 错误!没有从const_iterator // 到iterator隐式转换的途径 Iter i(const_cast<Iter>(ci)); // 仍是个错误!不能从const_iterator // 映射为iterator!

这里只是以deque为例,但是用其它容器类——list、set、multiset、map、multimap甚至条款25描述的hash表容器[1]——的结果一样。使用映射的行也许在vector或string的代码时能够编译,但这是我们马上要讨论的非常特殊的情形。

包含映射的代码不能通过编译的原因在于,对于这些容器而言,iterator和const_iterator是完全不同的类。它们之间并不比string和complex<float>具有更多的血缘关系。在两个毫无关联的类之间进行const_cast映射是荒谬的,所以reinterpret_cast、static_cast甚至C风格的映射也会导致同样的结果。

唉,不能编译的代码对于vector和string容器来说也许能够通过编译。那是因为通常情况下大多数实现都会采用真实的指针作为那些容器的迭代器。就这种实现而言,vector<T>::iterator是T*的typedef,而vector<T>::const_iterator是const T*的typedef,string::iterator是char*的typedef,而string::const_iterator是const char*的typedef。在这种实现的情况下,用const_cast把const_iterator映射成iterator当然可以编译而且没有问题,因为const_iterator与iterator之间的const_cast映射被最终解释成const T*到T*的映射。但是,即使是在这种实现中,reverse_iterator和const_reverse_iterator也是真正的类,所以你仍然不能直接用const_cast把const_reverse_iterator映射成reverse_iterator。而且,正如条款50解释的,这些实现通常只会在Release模式时才使用指针表示vector和string的迭代器[2]。所有这些事实表明,把const迭代器映射为迭代器是病态的,即使是对vector和string来说也时,因为移植性很值得怀疑。

如果你得到一个const_iterator并且可以访问它所指向的容器,那么有一种安全的、可移植的方法获取它所对应的iterator,而且,用不着陷入类型系统的转换。下面是解决思路的本质,虽然在它编译前还要稍作修改:

typedef deque<int> IntDeque; // 和以前一样 typedef IntDeque::iterator Iter; typedef IntDeque::const_iterator ConstIter; IntDeque d; ConstIter ci; ... // 让ci指向d Iter i(d.begin()); // 初始化i为d.begin() advance(i, distance(i, ci));

本文地址:http://com.8s8s.com/it/it28976.htm