Обратный итератор C ++ 11 с циклом while - PullRequest
0 голосов
/ 07 сентября 2018

Я знаю, что код не является хорошей практикой, поэтому вопрос не об этом.Я просто хочу понять, как работает следующий пример.Заметьте, я ничего не делаю с итератором, когда вызываю remove, поэтому, когда цикл переходит к следующей итерации, как он указывает на следующий элемент?

#include <string>
#include <list>
#include <algorithm>
#include <iostream>

class Obj;
std::list<Obj> objs;

class Obj
{
public:
  Obj(const std::string& name, int age)
  : name_(name), age_(age)
  {}

  std::string name()
  {
    return name_;
  }

  int age()
  {
    return age_;
  }
private:
  std::string name_;
  int age_;
};


void remove(const std::string& name)
{
  auto it = find_if(objs.begin(), objs.end(),[name] (Obj& o) { return (o.name() == name); });
  if (it != objs.end())
  {
    std::cout << "removing " << it->name() << std::endl;
    objs.erase(it);
  }
}

int main()
{
  objs.emplace_back("bob", 31);
  objs.emplace_back("alice", 30);
  objs.emplace_back("kevin", 25);
  objs.emplace_back("tom", 45);
  objs.emplace_back("bart", 37);
  objs.emplace_back("koen", 48);
  objs.emplace_back("jef", 23);
  objs.emplace_back("sara", 22);

  auto it = objs.rbegin();
  while (it != objs.rend())
  {

   std::cout << it->name() << std::endl;

   if (it->name() == "tom")
   {
      remove(it->name()); //notice I don't do anything to change the iterator
   }
   else
   {
     ++it;
   }
  }
  return 0;
}

Ниже выводится:

sara
jef
koen
bart
tom
removing tom
kevin
alice
bob

Ответы [ 2 ]

0 голосов
/ 07 сентября 2018

Мой другой ответ был неверным. Наблюдаемое поведение обусловлено реализацией reverse_iterator. От cppreference :

std::reverse_iterator - это итератор , адаптер , который меняет направление заданного итератора. Другими словами, когда предоставляется двунаправленный итератор, std::reverse_iterator создает новый итератор, который перемещается от конца к началу последовательности, определенной базовым двунаправленным итератором.

Для обратного итератора r, построенного из итератора i, соотношение &*r == &*(i-1) всегда истинно (если r разыменовано); таким образом, обратный итератор, построенный из итератора «один за другим», разыменовывает последний элемент в последовательности.

(выделено мое). Смотрите также [reverse.iterator] .

Хорошо, что это значит для нас: когда обратный итератор it указывает на "tom", он фактически оборачивает прямой итератор в элемент next , "bart". Когда вы отмените его, он берет элемент, предшествующий обернутому итератору, то есть один перед «bart», который действительно является «tom».

Когда вы удаляете "том", упакованный итератор не изменяется. (Ни один из них не признан недействительным.) Он по-прежнему указывает на «Барта». При разыменовании обратного итератора он ищет то, что предшествует «bart», который теперь является «кевином».

Это означает, что вы на самом деле не вызываете неопределенное поведение. Вы бы, если бы вы позвонили remove("bart") по линии 60.

0 голосов
/ 07 сентября 2018

Вы лишаете законной силы итератор, удаляя объект, к которому он обращается (независимо от того, используете ли вы значение этого для этой цели). Если вы попытаетесь получить к нему доступ после этого, поведение будет неопределенным (читай: может произойти все что угодно, например, тот же it переход к следующему элементу или сбой вашей программы). Вы не можете полагаться на это на любое другое поведение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...