简体   繁体   English

具有while循环的C ++ 11反向迭代器

[英]C++11 reverse iterator with while loop

I know the code isn't good practice, so the question is not about that. 我知道代码不是一个好习惯,所以问题不在于此。 I just want to understand how the following example works. 我只想了解以下示例的工作方式。 Notice I don't do anything with the iterator when I call remove, so when the loop goes to the next iteration how is it pointing to the next element? 注意,当我调用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;
}

Following is the output: 以下是输出:

sara
jef
koen
bart
tom
removing tom
kevin
alice
bob

You invalidate the iterator by removing the object it addresses (regardless of whether you use the value of it for that purpose). 您可以通过删除迭代器所针对的对象来使其无效 (无论是否为此目的使用了它的值)。 If you try to access it after that, the behaviour is undefined (read: anything can happen, like the same it jumping to the next element, or your program crashing). 如果你尝试之后访问它的行为是不确定的(阅读:什么都可能发生,就像同it跳跃到下一个元素,或者你的程序崩溃)。 You can't rely on this on any other behaviour. 您不能将其依赖于其他任何行为。

My other answer was not right. 我的其他答案不正确。 The observed behaviour is due to the implementation of reverse_iterator . 观察到的行为归因于reverse_iterator的实现。 From cppreference : 来自cppreference

std::reverse_iterator is an iterator adaptor that reverses the direction of a given iterator. std::reverse_iterator是一个迭代器适配器 ,可反转给定迭代器的方向。 In other words, when provided with a bidirectional iterator, std::reverse_iterator produces a new iterator that moves from the end to the beginning of the sequence defined by the underlying bidirectional iterator. 换句话说,当提供了双向迭代器时, std::reverse_iterator会生成一个新的迭代器,该迭代器将从底层双向迭代器定义的序列的末尾开始移动。

For a reverse iterator r constructed from an iterator i , the relationship &*r == &*(i-1) is always true (as long as r is dereferenceable); 对于由迭代器i构造的反向迭代器r ,关系&*r == &*(i-1)始终为真(只要r是可取消引用的); thus a reverse iterator constructed from a one-past-the-end iterator dereferences to the last element in a sequence. 因此,从一端到最后的迭代器构造的反向迭代器将引用序列中的最后一个元素。

(emphasis mine). (强调我的)。 See also [reverse.iterator] . 另请参见[reverse.iterator]

OK, what this means for us: when a reverse iterator it points to "tom", it actually wraps around a forward iterator to the next element, "bart". OK,这是什么意思为我们:当一个反向迭代it指向“汤姆”,它实际上环绕一个前向迭代到下一个元素,“捷运”。 When you derefer it, it takes an element preceding the wrapped iterator, ie, one before "bart", which indeed is "tom". 当您取消引用它时,它将在包装的迭代器之前使用一个元素,即在“ bart”之前的元素,后者实际上是“ tom”。

When you remove "tom", the wrapped iterator does not change. 当您删除“ tom”时,包装的迭代器不会更改。 (Neither it is invalidated.) It still points to "bart". (它们都没有失效。)它仍然指向“ bart”。 When you derefer the reverse iterator, it looks for what precedes "bart", which now is "kevin". 当您取消引用反向迭代器时,它将查找“ bart”( 现在为“ kevin”)之前的内容。

This means you don't really cause undefined behaviour. 这意味着您不会真正导致不确定的行为。 You would if you called remove("bart") at line 60. 如果在第60行调用了remove("bart") ,则可能会。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM