Почему удаление элемента _first_ в списке делает недействительным `.rend()`?

Протестировано в Mac OS X с помощью XCode 4.6.

В этом примере кода показано удаление последнего элемента 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
}

Ответ 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

Это относится не только к спискам, но и ко всем коллекциям, которые предлагают двунаправленные итераторы.