end iterator是否可移植性递减?

刚刚在我公司的源代码中遇到了end()迭代器的递减,这对我来说看起来很奇怪。 据我记忆,这是在一些平台上工作,但不为其他人。 也许我错了,但是我找不到任何有用的标准。 标准只说end()返回一个迭代器,它是过去的最终值,但是它保证是可减less的? 这样的代码如何符合标准?

 std::list<int>::iterator it = --l.end(); 

提前致谢。

我认为这是相关条款:

ISO / IEC 14882:2003 C ++标准23.1.1 / 12 – 序列

表68列出了为某些types的顺序容器提供的序列操作,但不包括其他types的容器。 对于“集装箱”一栏中显示的所有集装箱types,执行应提供这些操作,并执行这些操作以便分摊恒定时间。

     + ------------------------------------------------- --------------------------- +
     | 表68 |
     + -------------- + ----------------- + ---------------- ----- + --------------------- +
     | expression式| 返回types| 操作| 容器|
     |  |  | 语义|  |
     + -------------- + ----------------- + ---------------- ----- + --------------------- +
     |  a.front()| 参考;  |  * a.begin()| 向量,列表,deque |
     |  |  const_reference |  |  |
     |  | 对于常量a |  |  |
     + -------------- + ----------------- + ---------------- ----- + --------------------- +
     |  a.back()| 参考;  |  *  -  a.end()| 向量,列表,deque |
     |  |  const_reference |  |  |
     |  | 对于常量a |  |  |
     .................................................. ............................
     。  。  。  。  。
     。  。  。  。  。
     .................................................. ............................
     |  a.pop_back()|  void |  a.erase( -  a.end())| 向量,列表,deque |
     .................................................. ............................
     。  。  。  。  。
     。  。  。  。  。

因此,对于列出的容器,不仅应该从end()返回的迭代器是可递减的,递减的迭代器也应该是可取的。 (当然,除非容器是空的,调用未定义的行为。)

实际上,Visual C ++编译器附带的vectorlistdeque实现与表完全一样。 当然,这并不意味着每个编译器都这样做:

 // From VC++'s <list> implementation reference back() { // return last element of mutable sequence return (*(--end())); } const_reference back() const { // return last element of nonmutable sequence return (*(--end())); } 

请注意表中的代码:

ISO / IEC 14882:2003 C ++标准17.3.1.2/6 – 要求

在某些情况下,语义要求被表示为C ++代码。 这样的代码旨在作为一个构造与另一个构造的等价规范 ,而不一定是构造必须实现的方式。

所以虽然实现可能不是以begin()end()实现这些expression式,但C ++标准指定这两个expression式是等价的。 换句话说, a.back()*--a.end()是根据上述条款的等价结构。 在我看来,这意味着你应该能够用*--a.end()来代替a.back()每个实例,反之亦然,并且代码仍然可以工作。


根据Bo Persson的说法,我手头上的C ++标准的修订在表68中有一个缺陷 。

build议的决议:

在23.1.1 / 12的表68“可选顺序操作”中更改“a.back()”的规格

 *--a.end() 

 { iterator tmp = a.end(); --tmp; return *tmp; } 

和“a.pop_back()”的规范

 a.erase(--a.end()) 

 { iterator tmp = a.end(); --tmp; a.erase(tmp); } 

看起来你仍然可以递减从end()返回的迭代器,并且只要它不是临时的,就可以对递减的迭代器进行解引用。

这个代码是不好的,如果列表是空的,你有麻烦。

所以检查一下,如果列表不是空的代码是非常好的。