+-- v.begin() +-- v.end()
| |
v v
+---+---+---+---+---+---+ - +
| o | o | o | o | o | o | x |
+---+---+---+---+---+---+ - +
+ - +---+---+---+---+---+---+
| x | o | o | o | o | o | o |
+ - +---+---+---+---+---+---+
^ ^
| |
+-- v.rend() +-- v.rbegin()
(从这个答案复制并编辑了ASCII,这实际上促使我提出当前的问题。)
我确实看到了拥有
&*rit == &*(rit.base() - 1)
的优势,因为这样我就可以为任何反向迭代器 rit.base()
获取 rit
并且我将始终获得有效的迭代器。
但同时
v.rbegin().base()
;我得记得先减1,*(v.rbegin().base() - 1)
,v.rend().base() - 1
正是因为我无法取消引用 v.rend()
。如果设计是那样的
&*rit == &*rit.base()
呢?
v.rend().base()
,这是真的,但这只是对应于当前设计中无法取消引用 v.rend().base() - 1
;v.end()
,甚至是最接近的迭代器 b.rbegin()
,但我们必须在当前设计中将其添加到 - 1
才能获得向前迭代器到与反向相同的元素。
rit.base()
(事实上)还是
&*rit == &*(rit.base() - 1)
,我们都会拥有相同的便利
&*rit == &*rit.base()
rit.base()
无法在实际设计中取消引用所有有效的
- 1
rit.base()
+1
才能在替代设计中获得 v.rbegin()
,
所以我的问题是:做出确实做出的选择是否有明确的优势?或者这只是一枚抛硬币?
我看到第一个元素之前没有有效的迭代器,但这就是我的观点。当引入反向迭代器时,将
v.end()
设为像
std::prev(v.begin())
一样有效、不可解引用的迭代器会有什么问题?
事后看来,我确实看到了一个不容置疑的优势。我没有看到
v.end()
/
v.rbegin().base() == v.end()
的优势,因为为什么我想从它们的反向对应物创建 v.rend().base() == v.begin()
/v.end()
?但是,如果我有两个反向迭代器 v.begin()
和
rit1
定义范围 rit2
,那么采用 (rit1, rit2]
和 it1 = rit1.base()
可以轻松地以相反的方向引用相同范围的元素,it2 = rit2.base()
.长话短说:_我应该首先阅读 C++ 标准库 - 第 2 版中的第 §9.4.1 节。这不是设计决定,而是必然。
当你有一个包含 6 个条目的集合时(如你的图片所示),那么你所拥有的只是 7 个可用的迭代器值,仅此而已(6 个是可取消引用的,一个是最终迭代器值)。这就是反向迭代器可以构建的全部内容。
由于没有前一迭代器(就像第二张图片让人想到的那样),因此反向迭代器除了将
[it1, it2)
rbegin()
和
end()
到
rend()
之外没有其他选择。也就是说,你有
begin()
反过来,可解引用迭代器(从
(rbegin() + i).base() == end() - i
开始的前 6 个)实际上必须解引用迭代器 rbegin()
。没有其他方法可以实现反向迭代器。在当前的设计中,所有这些都按预期工作:
.base()-1
在你的替代设计中,不可能让所有这些都保持住。如果基础
std::make_reverse_iterator(it).base() == it;
auto rbegin = std::make_reverse_iterator(end);
auto rend = std::make_reverse_iterator(begin);
那么您必须使用偏移量进行构造:
&*rit == &*rit.base()
这需要将迭代器的概念引入“第一个元素之前”,而“第一个元素之前”并不存在。当前设计中不允许使用
auto last = std::prev(end)
auto rbegin = std::make_reverse_iterator(last);
auto pastbegin = std::prev(begin); // not allowed!
auto rend = std::make_reverse_iterator(pastbegin);
,并且允许它将使迭代器为指针的数组不可能具有反向迭代器,因为该语言禁止在第一个元素之前创建指针。否则施工后必须偏移
std::prev(begin)
这种方法只需要为反向迭代器引入过去的开始。由于它们不是指针,这在理论上是可能的,但这将需要额外的开销来表示比基本迭代器更多的迭代器状态。
这两种选择都不是特别方便使用。