std::list 中的 rbegin() 永远不会等于指向列表的迭代器?

rbegin() in std::list is never equal to an iterator pointing into the list?

本文关键字:于指 列表 迭代器 中的 list rbegin 永远 std      更新时间:2023-10-16

为什么下面的代码会崩溃?

int main(int argc, const char * argv[]) {

std::list<int> aList={1,2,3,4,5};
std::list<int>::reverse_iterator i=aList.rbegin();
i++;
i++;
assert(*i==3);//assertion passes as expected

while (i!=aList.rbegin()) { //never becomes false
aList.pop_back(); //segmentation fault
}
assert(*(aList.rbegin())==3);
return 0;
}

我假设 rbegin 最终会等于i并停止循环;然而没有发生。

注意我做了以下解决方法,仍然好奇上面的代码首先有什么问题

size_t  differance =std::distance( aList.rbegin(),i);
while (differance >0) {
aList.pop_back();
differance--;
}
assert(*aList.rbegin()==3);

std::list::rbegin()返回一个迭代器,该迭代器引用哨兵列表末尾节点,但取消引用存储在前一个节点中的值(在本例中为 5)。 此行为适用于std::list的任何反向迭代器:反向迭代器在取消引用反向迭代器时从中获取值的节点是迭代器实际指向的节点之前的节点。

像这样可视化它,根据迭代器引用的元素:

rend() <-------- rbegin()
|                 |
1     2 3 4 5   (end)
|                 |
begin() --------> end()

这意味着取消引用值 3 的迭代器实际上是在内部引用值为 4 的元素。当值 4 从列表中删除时,存储在i中的迭代器值将失效,此时您无法推理其行为。 它可能(并且应该)不等于所有其他迭代器。

您可以通过在*i == 3时写出*(i.base())的值来自己验证是否是这种情况。 你会看到*(i.base()) == 4.

因此,您的问题不在于迭代器rbegin()"永远不会等于指向列表的迭代器",而是您正在删除迭代器内部指向的元素,然后尝试将现在无效的迭代器与rbegin()进行比较。

考虑使用aList.erase()而不是循环。 由于您要从值为 4 的元素中删除数组的末尾,i.base()已经为您提供了一个迭代器,您可以将其直接传递给aList.erase()

aList.erase(i.base(), aList.end());

看起来你已经全部颠倒了,递增反向迭代器会向后移动它。

并且pop_back()将使最后一个元素的迭代器无效,因此循环中存在段错误。您应该做一个pop_front()并检查(i !=rend())