为什么是reverse_iterator::base偏移量?

问题描述 投票:0回答:2
      +-- 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 节。

这不是设计决定,而是必然。
c++ stl iterator language-lawyer reverse-iterator
2个回答
8
投票
反向迭代器并不是一个神奇的东西,它能够以某种方式反向迭代一个范围。它是一个建立在已有事物之上的立面。

当你有一个包含 6 个条目的集合时(如你的图片所示),那么你所拥有的只是 7 个可用的迭代器值,仅此而已(6 个是可取消引用的,一个是最终迭代器值)。这就是反向迭代器可以构建的全部内容。

由于没有前一迭代器(就像第二张图片让人想到的那样),因此反向迭代器除了将

[it1, it2)

映射到

rbegin()

end()
rend()
 之外没有其他选择。也就是说,你有
begin()

反过来,可解引用迭代器(从 
(rbegin() + i).base() == end() - i

开始的前 6 个)实际上必须解引用迭代器

rbegin()

。没有其他方法可以实现反向迭代器。

在当前的设计中,所有这些都按预期工作:

3
投票
.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)

    这种方法只需要为反向迭代器引入过去的开始。由于它们不是指针,这在理论上是可能的,但这将需要额外的开销来表示比基本迭代器更多的迭代器状态。
    
    

    这两种选择都不是特别方便使用。

© www.soinside.com 2019 - 2024. All rights reserved.