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 ++编译器附带的vector
, list
和deque
实现与表完全一样。 当然,这并不意味着每个编译器都这样做:
// 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()
返回的迭代器,并且只要它不是临时的,就可以对递减的迭代器进行解引用。
这个代码是不好的,如果列表是空的,你有麻烦。
所以检查一下,如果列表不是空的代码是非常好的。