标准::列表是循环的吗?

Is std::list circular?

本文关键字:列表 标准 循环      更新时间:2023-10-16

std::list 是一个循环的双向链表吗?使用它我可以执行以下操作:

#include <list>
int main(){
std::list<int> l = {10, 11, 12};
std::list<int>::iterator it = l.end();
it++;
std::cout << *it << std::endl; // prints 10
}

不,它不是一个循环的双向链表。

代码的行为是未定义的(由于迭代器的增量设置为end()(,仅此而已。

这个问题实际上有两个层次:


语言标准语义答案

这个答案已经由拔示巴给出,基本上是std::list<>的规定行为不包括任何循环。当您将迭代器递增到列表末尾时,您将进入未定义行为的区域,并且所有赌注都关闭。您的程序可以打印"You have be pwnd!"而不是"10"。


实现细节答案

该标准没有规定std::list<>是否是循环的。它只描述一些可观察的行为,而没有定义其他一些行为。这允许std::list<>的实施者以他们认为合适的方式实现列表。

查看std::list<>的要求,我们看到它

  • 必须支持双向迭代器,并且

  • 必须在恒定时间内提供end()迭代器。

将两者放在一起,我们看到递减end()迭代器在非空列表中得到了很好的定义,并且必须为该列表的最后一个元素生成一个迭代器。

对于实施者来说,这意味着

  • 列表应该是双重链接的(以支持递增和递减(,

  • 必须有一个end()可以返回的虚拟迭代器,并且

  • 虚拟迭代器实际上必须包含指向列表最后一个元素的指针。

可以使用以下任一方式实现这样的双链表:

  • 指向列表自己的对象中的第一个元素的指针 + 仅包含指向最后一个元素的指针的虚拟迭代器。

  • 两个虚拟迭代器,一个在第一个元素之前,一个在最后一个元素之后。开头的迭代器只会提供一个前向指针,最后的迭代器只会提供一个向后指针。

  • 或者只是将两个虚拟迭代器融合到一个对象中,同时使用向前和向后指针,并使其仅在不包含任何数据时才具有特殊性。这就是循环列表方法。

前两种方法引入了对第一个元素、最后一个元素和空列表的大量特殊处理。那不好。第三种方法没有这种特殊处理:空列表只是在向前和向后引用上将虚拟人链接到自身,任何列表项的添加/删除都只是在两个已经存在的迭代器之间添加/删除迭代器。这大大简化了代码。

因此,大多数实现者会选择循环实现。这是理智的选择。编写您正在使用的std::list<>的人似乎是理智的那种。但不能保证。他们可以毫无理由地用更复杂的实现替换当前的实现,并将其与标准C++库实现的下一个版本一起发布。您的程序可能不再打印10,甚至根本不打印任何内容。它也可以安装一个比特币矿工。C++标准不会在乎:只要std::list<>提供了规定的行为,当你越过标有"未定义行为"的界限时,实现就可以自由地做任何它喜欢的废话。