刚刚在公司的源代码中遇到了end()
迭代器的减量,对我来说这看起来很奇怪。据我所知,这是在一些平台上工作,但不适用于其他平台。也许我错了,但是在标准方面我找不到任何有用的东西。标准只说end()
返回一个迭代器,它是过去的结束值,但它是否保证可递减?这样的代码如何与标准相匹配?
std::list<int>::iterator it = --l.end();
提前致谢。
答案 0 :(得分:50)
我认为这是相关条款:
ISO / IEC 14882:2003 C ++标准23.1.1 / 12 - 序列
表68列出了序列操作 为某些类型的提供 顺序容器而不是其他容器。 实施应提供这些 所有容器类型的操作 显示在“容器”栏中,和 应实施它们以便采取 摊销不变的时间。
+----------------------------------------------------------------------------+ | Table 68 | +--------------+-----------------+---------------------+---------------------+ | expression | return type | operational | container | | | | semantics | | +--------------+-----------------+---------------------+---------------------+ | a.front() | reference; | *a.begin() | vector, list, deque | | | const_reference | | | | | for constant a | | | +--------------+-----------------+---------------------+---------------------+ | a.back() | reference; | *--a.end() | vector, list, deque | | | const_reference | | | | | for constant a | | | .............................................................................. . . . . . . . . . . .............................................................................. | a.pop_back() | void | a.erase(--a.end()) | vector, list, 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()
方面实现这些表达式,但C ++标准指定这两个表达式是等效的。换句话说,a.back()
和*--a.end()
是根据上述条款的等效结构。在我看来,这意味着您应该能够用a.back()
替换*--a.end()
的每个实例,反之亦然,并且代码仍然有用。
根据Bo Persson的说法,我手边的{+ 3}}修订了C ++标准。
提议的决议:
更改表68中的规格 “可选序列操作” 23.1.1 / 12来自
的“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()
返回的迭代器,并取消引用递减的迭代器,只要它不是临时的。
答案 1 :(得分:2)
来自std::prev
的文档
尽管表达式--c.end()通常会编译,但不能保证这样做:c.end()是一个右值表达式,并且没有迭代器要求指定保证右值递减工作。特别是,当迭代器实现为指针时,--c.end()不会编译,而std :: prev(c.end())会编译。
这意味着前缀递减操作的实现可能不是inside class形式的iterator iterator::operator--(int)
,而是重载了类iterator operator--(iterator&, int)
之外的类。
,因此您应该选择std::prev
或执行以下操作:
{ auto end = a.end(); --end; };
答案 2 :(得分:0)
此代码不正常,如果列表为空,则表示您遇到问题。
因此检查一下,如果列表不为空,则代码非常精细。