led*_*kol 52 c++ portability iterator stl
刚刚end()在我的公司源代码中遇到了迭代器的减少,对我来说它看起来很奇怪.据我所知,这是在一些平台上工作,但不适用于其他平台.也许我错了,但是在标准方面我找不到任何有用的东西.标准只表示end()返回一个迭代器,它是一个过去的结束值,但它是否保证可递减?这样的代码如何符合标准?
std::list<int>::iterator it = --l.end();
Run Code Online (Sandbox Code Playgroud)
提前致谢.
In *_*ico 51
我认为这是相关条款:
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()可递减的,而且递减的迭代器也应该是不可解的.(当然,除非容器是空的.这会调用未定义的行为.)
其实vector,list和deque实现,与Visual C++编译器就做它酷似表.当然,这并不是说每个编译器都这样做:
// 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()));
}
Run Code Online (Sandbox Code Playgroud)
请注意表中的代码:
ISO/IEC 14882:2003 C++标准17.3.1.2/6 - 要求
在某些情况下,语义要求表示为C + +代码.此类代码旨在作为构造与另一构造的等同性的规范,而不一定是构造必须实现的方式.
因此,虽然实现可能无法根据begin()和实现这些表达式end(),但C++标准指定这两个表达式是等效的.换句话说,a.back()并且*--a.end()是根据上述条款的等同构造.在我看来,这意味着你应该能够更换的每个实例a.back()有*--a.end(),反之亦然,并让代码仍然可以工作.
据柏Persson的,C++标准的修订,我手头上有一个缺陷相对于表68.
拟议决议:
更改23.1.1/12中表68"可选序列操作"中的"a.back()"中的规范
Run Code Online (Sandbox Code Playgroud)*--a.end()至
Run Code Online (Sandbox Code Playgroud){ iterator tmp = a.end(); --tmp; return *tmp; }以及来自的"a.pop_back()"规范
Run Code Online (Sandbox Code Playgroud)a.erase(--a.end())至
Run Code Online (Sandbox Code Playgroud){ iterator tmp = a.end(); --tmp; a.erase(tmp); }
看起来你仍然可以减少从迭代器返回的迭代器,end()并取消引用递减的迭代器,只要它不是临时的.
从文档中 std::prev
尽管表达式 --c.end() 经常编译,但不能保证这样做:c.end() 是一个右值表达式,并且没有迭代器要求指定右值的递减保证工作。特别是,当迭代器被实现为指针时,--c.end() 不会编译,而 std::prev(c.end()) 会编译。
这意味着前缀递减操作的实现可能不是类内部形式,iterator iterator::operator--(int)而是类外部形式的重载iterator operator--(iterator&, int)。
所以你应该更喜欢std::prev或执行以下操作:
{ auto end = a.end(); --end; };