Effective STL [26] | 尽量用iterator代替const_iterator,reverse_iterator和const_reverse_iterator

警告
本文最后更新于 2023-08-17,文中内容可能已过时。

4种迭代器

每个标准容器类都提供4种迭代器类型:iterator,const_iteratorreverse_iteratorconst_reverse_iterator

对于container<T>而言,iterator的作用相当于T*,而const_iterator则相当于const T*

增加一个iterator或者const_iterator可以在一个从容器开头趋向尾部的遍历中让你移动到容器的下一个元素。

reverse_iteratorconst_reverse_iterator同样相当于对应的Tconst T,所不同的是,增加reverse_iterator或者const_reverse_iterator会在从尾到头的遍历中让你移动到容器的下一个元素。

vector的insert和erase的样式

1
2
3
iterator insert(iterator position, const T& x);
iterator erase(iterator position);
iterator erase(iterator rangeBegin, iterator rangeEnd);

这些方法只接受iterator类型的参数,而不是const_iteratorreverse_iteratorconst_reverse_iterator。总是iterator

迭代器之间存在的转换关系

图中显示了从iteratorconst_iterator、从iteratorreverse_iterator和从reverse_iteratorconst_reverse_iterator可以进行隐式转换。

并且,reverse_iterator可以通过调用其base成员函数转换为iteratorconst_reverse_iterator也可以类似地通过base转换成为const_iterator

通过base得到的也许并非你所期待的iterator。

而且,没有办法从一个const_iterator转换得到一个iterator,也无法从const_reverse_iterator得到reverse_iterator

所以,当你需要指出插入位置或删除的元素时,const迭代器几乎没有用。

尽量使用iterator取代const或者reverse类型的迭代器

  1. inserterase的一些版本要求iterator。如果你需要调用这些函数,你就必须产生iterator,而不能用constreverse iterators

  2. 不可能把const_iterator隐式转换成iterator。从一个const_iterator产生一个iterator的技术并不普遍适用,而且不保证高效。

  3. reverse_iterator转换而来的iterator在转换之后可能需要相应的调整。

iteratorreverse_iterator之间的选择显而易见——依赖于从前到后或从后到前的遍历。

迭代器比较

当在iteratorconst_iterator之间作选择的时候,你有更充分的理由选择iterator,即使const_iterator同样可行而且即使你并不需要调用容器类的任何成员函数。其中的令人讨厌的原因包括iteratorconst_iterator之间的比较:

1
2
3
4
5
6
7
typedef deque<int> IntDeque; // typedef可以极大地简化STL容器类和iterator的操作。
typedef IntDeque::iterator Iter;
typedef IntDeque::const_iterator ConstIter;
Iter i;
ConstIter ci;
... // 同一个容器
if (i == ci) ... // 比较iterator和const_iterator

唯一的变化是等号的一边的类型是iterator,而另一边的类型是const_iterator

因为iterator应该在比较之前隐式的转换成const_iterator,真正的比较应该在两个const_iterator之间进行。

如果一些实现将const_iteratoroperator==作为const_iterator的一个成员函数而不是非成员函数。

而问题的解决之道显得非常有趣:只要像这样交换两个iterator的位置:

1
if (ci == i)... // 当上面比较无法通过编译时的解决方法

迭代器混用

不仅是比较是否相等,只要你在同一个表达式中混用iteratorconst_iterator(或者reverse_iteratorconst_reverse_iterator),这样的问题就可能会出现:

1
if (i - ci >= 3) ... // 如果i与ci之间至少有三个元素...

如果迭代器的类型不同,你的正确的代码可能会被错误地拒绝。

本例中最简单的解决方法是通过一个(安全的)映射把iterator转换为const_iterator:

1
if (static_cast<ConstIter>(i) - ci >= 3) ... // 当上面的代码无法通过编译时的解决方法

避免这类问题的最简单的方法是减少混用不同类型的迭代器的机会。

Buy me a coffee~
支付宝
微信
0%