为啥删除列表的 _first_ 元素会使 `.rend()` 无效?
Posted
技术标签:
【中文标题】为啥删除列表的 _first_ 元素会使 `.rend()` 无效?【英文标题】:Why does removing the _first_ element of a list invalidate `.rend()`?为什么删除列表的 _first_ 元素会使 `.rend()` 无效? 【发布时间】:2013-02-07 20:13:19 【问题描述】:使用 XCode 4.6 在 Mac OS X 上测试。
此示例代码显示删除 std::list
的最后一个元素按我的预期工作:对 list::end()
的迭代器引用仍然是“结束后 1”并且仍然有效,即使通过删除的最后一个元素。
但第二个例子与我的直觉相反。删除列表的 first 元素更改list::rend()
,我认为这是“1 过去的开头”。
我的预期错了吗?为什么错了?为什么通过删除最后一个元素对“结束后的 1”的引用仍然有效(不应该吗?),但在删除前面的元素后对“开始前的 1”(.rend()
)的引用变得无效?
void printList( list<int>& os )
for( int& i : os )
printf( "%d ", i ) ;
puts("");
void testList()
list< int > os ;
os.push_back( 1 ) ;
os.push_back( 2 ) ;
os.push_back( 3 ) ;
os.push_back( 4 ) ;
os.push_back( 5 ) ;
// Forward iterators: reference to .end() not invalidated when remove last elt.
list<int>::iterator fwdEnd = os.end() ;
printList( os ) ;
os.erase( --os.end() ) ; // remove the 5 (last elt)
printList( os ) ;
if( fwdEnd == os.end() ) puts( "YES, fwdEnd==os.end() still, iterators not invalidated" ) ; // I get __this__ result
else puts( "NO: fwdEnd INVALIDATED" ) ;
list<int>::reverse_iterator revEnd = os.rend() ;
// remove the front element
printList( os ) ;
os.erase( os.begin() ) ; // removes the 1
printList( os ) ;
if( revEnd == os.rend() ) puts( "YES revEnd is still valid" ) ;
else puts( "NO: revEnd NOT valid" ) ; // I get __this__ result
【问题讨论】:
按照我的理解,反向迭代器并不指向它看似指向的东西。 “一个过去的开头”反向迭代器包装了一个引用开头的普通插入器。当包装的迭代器失效时,反向迭代器也是如此。但我没有引用标准来支持这一点。 我猜另一个问题是:“rbegin
在第一个示例中是否同样无效?”
【参考方案1】:
这是因为反向迭代器的引用逻辑与常规迭代器略有不同:它指向一个元素,但在取消引用时,它会产生对前一个元素的引用。
如果您尝试以下操作,您将很容易看到这一点:
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
vector<int> v = 1, 2, 3, 4, 5, 6 ;
auto i = find(begin(v), end(v), 3);
cout << *i << endl;
vector<int>::const_reverse_iterator ri(i);
cout << *ri << endl;
输出应该是:
3
2
当反向迭代器物理上指向某个元素时,它逻辑上指向它之前的元素。因此,在物理上指向索引为 i
的集合中元素的反向迭代器在取消引用时会产生(引用)索引为 i-1
的元素:
i, *i
|
- 1 2 3 4 5 6 -
| |
*ri ri
这就是为什么 rend()
返回的迭代器实际上指向集合中的第一个元素,而不是第一个元素之前的元素。因此,删除第一个元素会使其无效。
begin, *begin end, *end
| |
- 1 2 3 4 5 6 -
| | | |
*rend rend *rbegin rbegin
这不仅适用于列表,还适用于所有提供双向迭代器的集合。
【讨论】:
所以 没有真正的元素 称为“开头前面的 1”.. 而正向迭代器实际上指向“结束后的 1”,反向迭代器只是指向开头,取消引用时,指向“开头前面的 1” @bobobobo:我想有人可以这么说,尽管从技术上讲,甚至没有一个“在末尾称为 1 的元素”:迭代器只返回一个 position byend()
指向的,就是最后一个元素位置之后的位置。但我想这毕竟是你想说的,在这种情况下你是对的,rend()
返回的迭代器物理上没有指向“集合开始之前的位置”。以上是关于为啥删除列表的 _first_ 元素会使 `.rend()` 无效?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在已删除的默认 ctor 旁边定义一个空的副本 ctor 会使空列表的值初始化失败?