C++ 为什么删除列表中的_first_元素会使“.rend()”无效?

C++ 为什么删除列表中的_first_元素会使“.rend()”无效?,c++,stdlist,reverse-iterator,C++,Stdlist,Reverse Iterator,使用XCode 4.6在Mac OS X上测试 此示例代码显示删除std::list的最后一个元素的效果与我预期的一样:对list::end()的迭代器引用仍然是“1过了结尾”,并且仍然有效,即使删除了最后一个元素 但第二个例子与我的直觉相反。删除列表的第一个元素会改变list::rend(),我认为这是“1过了开头” 我的期望错了吗?为什么错了?为什么通过删除最后一个元素而对“1超过结束”的引用仍然有效(是否应该?),但删除前一个元素后,对“1在开始(.rend())前面的引用变得无效 voi

使用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
}
void打印列表(列表和操作系统)
{
用于(int&i:os)
printf(“%d”,i);
认沽权(“”);
}
void testList()
{
列表os;
os.推回(1);
os.推回(2);
os.推回(3);
os.推回(4);
os.推回(5);
//正向迭代器:删除最后一个elt时,对.end()的引用不会失效。
列表::迭代器fwdendd=os.end();
打印列表(os);
os.erase(--os.end());//删除5(最后一个elt)
打印列表(os);
如果(fwdendd==os.end())放置(“是的,fwdendd==os.end(),迭代器未失效”);//我得到这个结果
其他认沽权证(“否:失效”);
list::reverse_迭代器revEnd=os.rend();
//拆下前滤芯
打印列表(os);
os.erase(os.begin());//删除1
打印列表(os);
如果(revEnd==os.rend())放入(“YES revEnd仍然有效”);
else puts(“否:revEnd无效”);//我得到这个结果
}

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

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

#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;
}
当反向迭代器物理上指向某个元素时,它在逻辑上指向它前面的元素。因此,反向迭代器物理上指向索引为
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

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

我的理解是,反向迭代器并不指向它似乎指向的对象。一个“一过开头”反向迭代器包装了一个引用开始的普通迭代器。当包装的迭代器失效时,反向迭代器也失效。但我没有引用标准来支持这一点。我想另一个问题是:“在第一个例子中,
rbegin
是否同样无效?”因此没有实际的元素称为“开头前面的1”。。前向迭代器实际上指向“1过了终点”,而反向迭代器只指向起点,当取消引用时,指向“1在起点前面”@bobobo:我想人们可以这么说,尽管从技术上讲,甚至没有一个“元素称为1过终点”:迭代器只通过
end()返回一个位置
指向,这是超过最后一个元素位置的位置。但我想这就是你到底想说的,在这种情况下你是对的,
rend()
返回的迭代器实际指向的“集合开始之前的位置”。
           begin, *begin                       end, *end
           |                                   |
    -      1     2     3     4     5     6     -
    |      |                             |     |
*rend      rend                    *rbegin     rbegin