问题描述
刚刚在我的公司源代码中遇到了 end()
迭代器的减量,这对我来说看起来很奇怪.据我记得,这适用于某些平台,但不适用于其他平台.也许我错了,但是我在标准中找不到任何有用的东西.标准只说 end()
返回一个迭代器,它是过去的值,但它保证是可递减的吗?这样的代码如何符合标准?
std::list
::iterator it = --l.end(); 提前致谢.
解决方案我认为这是相关的子句:
<块引用>ISO/IEC 14882:2003 C++ 标准 23.1.1/12 – 序列
表 68 列出了序列操作为某些类型的顺序容器,但不是其他容器.实现应提供这些所有容器类型的操作显示在容器"列中,以及应实施它们,以便采取摊销常数时间.
<上一页>+-----------------------------------------------------------------+|表 68 |+--------------+-----------------+---------------------+----------+|表达 |返回类型 |运营 |集装箱 ||||语义 ||+--------------+-----------------+---------------------+----------+|a.front() |参考;|*a.begin() |矢量、列表、双端队列 |||常量参考 |||||对于常数 a |||+--------------+-----------------+---------------------+----------+|a.back() |参考;|*--a.end() |矢量、列表、双端队列 |||常量参考 |||||对于常数 a |||................................................................................................................................................................|a.pop_back() |无效 |a.erase(--a.end()) |矢量、列表、双端队列 |.....................................................................................
所以对于列出的容器,从 end()
返回的迭代器不仅应该是可递减的,递减后的迭代器也应该是可解引用的.(当然,除非容器是空的.这会引发未定义的行为.)
事实上,Visual C++ 编译器附带的 vector
、list
和 deque
实现与表格完全一样.当然,这并不意味着每个编译器都这样做:
//来自 VC++ 的 <list>执行参考回(){//返回可变序列的最后一个元素返回 (*(--end()));}const_reference back() 常量{//返回非可变序列的最后一个元素返回 (*(--end()));}
<小时>
注意表中的代码:
<块引用>ISO/IEC 14882:2003 C++ 标准 17.3.1.2/6 - 要求
在某些情况下,语义要求被呈现为 C++代码.此类代码旨在作为等价的规范构造到另一个构造,而不是必然作为构造的方式必须执行.
因此,虽然实现可能不会根据 begin()
和 end()
实现这些表达式,但 C++ 标准规定这两个表达式是等价的.换句话说,a.back()
和 *--a.end()
是根据上述条款的等效结构.在我看来,这意味着您应该能够用 *--a.end()
替换 a.back()
的每个实例,反之亦然,让代码仍然有效.
根据 Bo Persson 的说法,我手头的 C++ 标准修订版表 68 有缺陷.
<块引用>建议的解决方案:
更改表 68 中的规格可选序列操作"中23.1.1/12 来自
的a.back()"*--a.end()
到
{ 迭代器 tmp = a.end();--tmp;返回 *tmp;}
和规范a.pop_back()"来自
a.erase(--a.end())
到
{ 迭代器 tmp = a.end();--tmp;a.擦除(tmp);}
看来您仍然可以递减从 end()
返回的迭代器并取消对递减后的迭代器的引用,只要它不是临时的.
Just encountered decrement of end()
iterator in my company source codes and it looks strange for me. As far as I remember this was working on some platforms, but not for the others. Maybe I'm wrong, however I couldn't find anything useful in standard about that. Standard only says that end()
returns an iterator which is the past-the-end value, but is it guaranteed to be decrementable? How does code like that match the standard?
std::list<int>::iterator it = --l.end();
Thanks in advance.
I think this is the relevant clause:
ISO/IEC 14882:2003 C++ Standard 23.1.1/12 – Sequences
Table 68 lists sequence operations that are provided for some types of sequential containers but not others. An implementation shall provide these operations for all container types shown in the "container" column, and shall implement them so as to take amortized constant time.
+----------------------------------------------------------------------------+ | 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 | .............................................................................. . . . . . . . . . .
So for the containers listed, not only should the iterator returned from end()
be decrementable, the decremented iterator should also be dereferencable. (Unless the container is empty, of course. That invokes undefined behavior.)
In fact, vector
, list
and deque
implementations that came with the Visual C++ compiler does it exactly like the table. Of course, that's not to imply that every compiler does it like this:
// 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()));
}
Note about the code in the table:
ISO/IEC 14882:2003 C++ Standard 17.3.1.2/6 – Requirements
In some cases the semantic requirements are presented as C + + code. Such code is intended as a specification of equivalence of a construct to another construct, not necessarily as the way the construct must be implemented.
So while it's true that an implementation may not implement those expressions in terms of begin()
and end()
, the C++ standard specifies that the two expressions are equivalent. In other words, a.back()
and *--a.end()
are equivalent constructs according to the above clause. It seems to me that it means that you should be able to replace every instance of a.back()
with *--a.end()
and vice-versa and have the code still work.
According to Bo Persson, the revision of the C++ standard that I have on hand has a defect with respect to Table 68.
Proposed resolution:
Change the specification in table 68 "Optional Sequence Operations" in 23.1.1/12 for "a.back()" from
*--a.end()
to
{ iterator tmp = a.end(); --tmp; return *tmp; }
and the specification for "a.pop_back()" from
a.erase(--a.end())
to
{ iterator tmp = a.end(); --tmp; a.erase(tmp); }
It appears that you can still decrement the iterator returned from end()
and dereference the decremented iterator, as long as it's not a temporary.
这篇关于结束迭代器递减的可移植性如何?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!