带 while 循环的 C++11 反向迭代器
C++11 reverse iterator with while loop
我知道代码不是很好的做法,所以问题不在于此。我只想了解以下示例的工作原理。请注意,当我调用 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
您通过删除它寻址的对象来使迭代器无效(无论您是否使用它的值用于此目的)。如果您在那之后尝试访问它,则行为是未定义的(阅读:任何事情都可能发生,例如 it
跳转到下一个元素,或者您的程序崩溃)。你不能依赖于任何其他行为。
我的另一个答案不对。观察到的行为是由于 reverse_iterator
的实施。来自 cppreference:
std::reverse_iterator
is an iterator adaptor that reverses the direction of a given 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.
For a reverse iterator r
constructed from an iterator i
, the relationship &*r == &*(i-1)
is always true (as long as r
is dereferenceable); thus a reverse iterator constructed from a one-past-the-end iterator dereferences to the last element in a sequence.
(强调我的)。另见 [reverse.iterator].
好的,这对我们意味着什么:当一个反向迭代器 it
指向“tom”时,它实际上环绕一个前向迭代器指向 next 元素, “巴特”。当您取消引用它时,它在包装的迭代器之前需要一个元素,即“bart”之前的一个元素,它确实是“tom”。
当您删除“tom”时,包装的迭代器不会改变。 (它也没有失效。)它仍然指向“bart”。当您取消引用反向迭代器时,它会查找“bart”之前的内容,现在 是“kevin”。
这意味着您并没有真正导致未定义的行为。如果您在第 60 行调用 remove("bart")
,您就会这样做。
我知道代码不是很好的做法,所以问题不在于此。我只想了解以下示例的工作原理。请注意,当我调用 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
您通过删除它寻址的对象来使迭代器无效(无论您是否使用它的值用于此目的)。如果您在那之后尝试访问它,则行为是未定义的(阅读:任何事情都可能发生,例如 it
跳转到下一个元素,或者您的程序崩溃)。你不能依赖于任何其他行为。
我的另一个答案不对。观察到的行为是由于 reverse_iterator
的实施。来自 cppreference:
std::reverse_iterator
is an iterator adaptor that reverses the direction of a given 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.For a reverse iterator
r
constructed from an iteratori
, the relationship&*r == &*(i-1)
is always true (as long asr
is dereferenceable); thus a reverse iterator constructed from a one-past-the-end iterator dereferences to the last element in a sequence.
(强调我的)。另见 [reverse.iterator].
好的,这对我们意味着什么:当一个反向迭代器 it
指向“tom”时,它实际上环绕一个前向迭代器指向 next 元素, “巴特”。当您取消引用它时,它在包装的迭代器之前需要一个元素,即“bart”之前的一个元素,它确实是“tom”。
当您删除“tom”时,包装的迭代器不会改变。 (它也没有失效。)它仍然指向“bart”。当您取消引用反向迭代器时,它会查找“bart”之前的内容,现在 是“kevin”。
这意味着您并没有真正导致未定义的行为。如果您在第 60 行调用 remove("bart")
,您就会这样做。