简体   繁体   English

为什么 std::list 上的 push_back 会更改用 rbegin 初始化的反向迭代器?

[英]Why does a push_back on an std::list change a reverse iterator initialized with rbegin?

According to some STL documentation I found, inserting or deleting elements in an std::list does not invalidate iterators.根据我发现的一些 STL 文档,在 std::list 中插入或删除元素不会使迭代器无效。 This means that it is allowed to loop over a list (from begin() to end() ), and then add elements using push_front.这意味着它可以循环遍历列表(从begin()end() ),然后使用 push_front 添加元素。

Eg, in the following code, I initialize a list with elements a, b and c, then loop over it and perform a push_front of the elements.例如,在下面的代码中,我用元素 a、b 和 c 初始化了一个列表,然后遍历它并对元素执行 push_front。 The result should be cbaabc, which is exactly what I get:结果应该是 cbaabc,这正是我得到的:

std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

for (std::list<std::string>::iterator itList = testList.begin(); itList != testList.end(); ++itList)
   testList.push_front(*itList);

for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
   std::cout << *itList << std::endl;

When I use reverse iterators (loop from rbegin() to rend() ) and use push_back, I would expect similar behavior, ie a result of abccba.当我使用反向迭代器(从rbegin()循环到rend() )并使用 push_back 时,我会期待类似的行为,即 abccba 的结果。 However, I get a different result:但是,我得到了不同的结果:

std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

for (std::list<std::string>::reverse_iterator itList = testList.rbegin(); itList != testList.rend(); ++itList)
   testList.push_back(*itList);

for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
   std::cout << *itList << std::endl;

The result is not abccba , but abcccba .结果不是abccba ,而是abcccba That's right there is one additional c added.没错,多了一个 c。

It looks like the first push_back also changes the value of the iterator that was initialized with rbegin().看起来第一个 push_back 也改变了用 rbegin() 初始化的迭代器的值。 After the push_back it does not point anymore to the 3rd element in the list (which was previously the last one), but to the 4th element (which is now the last one).在 push_back 之后,它不再指向列表中的第三个元素(以前是最后一个),而是指向第四个元素(现在是最后一个)。

I tested this with both Visual Studio 2010 and with GCC and both return the same result.我使用 Visual Studio 2010 和 GCC 对此进行了测试,两者都返回相同的结果。

Is this an error?这是一个错误吗? Or some strange behavior of reverse iterators that I'm not aware of?或者我不知道的反向迭代器的一些奇怪行为?

The standard says that iterators and references remain valid during an insert.该标准规定迭代器和引用在插入期间保持有效。 It doesn't say anything about reverse iterators.它没有说明任何关于反向迭代器的内容。 :-) :-)

The reverse_iterator returned by rbegin() internally holds the value of end() . rbegin()返回的reverse_iterator在内部保存end()的值。 After a push_back() this value will obviously not be the same as it was before.push_back()之后,这个值显然与之前不同。 I don't think the standard says what it should be.我认为标准没有说明它应该是什么。 Obvious alternatives include the previous last element of the list, or that it stays at the end if that is a fixed value (like a sentinel node).明显的替代方案包括列表的前一个最后一个元素,或者如果它是固定值(如哨兵节点),则它留在最后。


Technical details: The value returned by rend() cannot point before begin() , because that is not valid.技术细节: rend()返回的值不能指向begin()之前,因为那是无效的。 So it was decided that rend() should contain the value of begin() and all other reverse iterators be shifted one position further.因此决定rend()应该包含begin()的值,并且所有其他反向迭代器进一步移动一个 position。 The operator* compensates for this and accesses the correct element anyway. operator*对此进行补偿并访问正确的元素。

First paragraph of 24.5.1 Reverse iterators says: 24.5.1 反向迭代器的第一段说:

Class template reverse_iterator is an iterator adaptor that iterates from the end of the sequence defined by its underlying iterator to the beginning of that sequence. Class 模板reverse_iterator是一个迭代器适配器,它从其底层迭代器定义的序列末尾迭代到该序列的开头。 The fundamental relation between a reverse iterator and its corresponding iterator i is established by the identity:反向迭代器与其对应的迭代器 i 之间的基本关系由恒等式建立:
&*(reverse_iterator(i)) == &*(i - 1) . &*(reverse_iterator(i)) == &*(i - 1)

I think to understand this, it's best to start by re-casting the for loop as a while loop:我认为要理解这一点,最好先将for循环重新转换为while循环:

typedef std::list<std::string> container;

container testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

container::reverse_iterator itList = testList.rbegin(); 
while (itList != testList.rend()) {
    testList.push_back(*itList);
     ++itList;
}

Along with that, we have to understand how a reverse_iterator works in general.除此之外,我们还必须了解reverse_iterator通常是如何工作的。 Specifically a reverse_iterator really points to the element after the one you get when you dereference it.具体来说, reverse_iterator确实指向在取消引用时获得的元素之后的元素。 end() yields an iterator to just after the end of the container -- but for things like arrays, there's no defined way to point to just before the beginning of a container. end()产生一个指向容器末尾之后的迭代器——但是对于像 arrays 这样的东西,没有定义的方法指向容器开始之前。 What C++ does instead is have the iterator start from just after the end, and progress to the beginning, but when you dereference it, you get the element just before where it actually points. C++ 所做的是让迭代器从结束之后开始,然后前进到开始,但是当您取消引用它时,您会在它实际指向的位置之前获得元素。

That means your code actually works like this:这意味着您的代码实际上是这样工作的:

在此处输入图像描述

After that, you get pretty much what you expect, pushing back B and then A, so you end up with ABCCCBA.在那之后,你几乎得到了你所期望的,推回 B,然后是 A,所以你最终得到了 ABCCCBA。

Try using an iterator for both.尝试对两者都使用迭代器。 Try:尝试:

std::list<std::string>::iterator i = testList.end(); 

and reverse through with --i并通过 --i 反转

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

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