为什么删除列表的_first_元素会使`.rend()`无效

Why does removing the _first_ element of a list invalidate `.rend()`?

本文关键字:rend 无效 元素 删除列 删除 列表 first 为什么      更新时间:2023-10-16

使用XCode 4.6在Mac OS X上进行测试。

此示例代码显示,删除std::list的最后一个元素如我所料:对list::end()的迭代器引用仍然是"1过末尾",并且仍然有效,即使删除了最后一个元件

但第二个例子违背了我的直觉。删除列表第一个元素将更改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
}

这是因为反向迭代器的引用逻辑与常规迭代器略有不同:它指向一个元素,但当取消引用时,它会产生对前一个元素的引用。

如果你尝试以下操作,你会很容易看到这一点:

#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

这不仅适用于列表,而且适用于所有提供双向迭代器的集合。