C++ std::vector::insert vs std::list::operator[]

C++ std::vector::insert vs std::list::operator[],c++,list,vector,C++,List,Vector,我知道没有实现std::list::operator[],因为它的性能很差。但是std::vector::insert呢它的效率和std::list::operator[]一样低。背后的解释是什么?std::list::operator[]需要进行O(N)遍历,这与list的设计目的并不一致。如果需要运算符[],请使用不同的容器类型。当C++用户看到一个 []/Cord>时,他们假定O(1)(或者,更糟的是O(log n))操作。为列表提供[]将打破这一局面 但是,尽管std::vector::

我知道没有实现
std::list::operator[]
,因为它的性能很差。但是
std::vector::insert
呢它的效率和
std::list::operator[]
一样低。背后的解释是什么?

std::list::operator[]
需要进行O(N)遍历,这与
list
的设计目的并不一致。如果需要
运算符[]
,请使用不同的容器类型。当C++用户看到一个<代码> []/Cord>时,他们假定O(1)(或者,更糟的是O(log n))操作。为
列表提供
[]
将打破这一局面


但是,尽管
std::vector::insert
也是O(N),但它可以得到优化:通过将
vector
的容量成片增长,可以很容易地优化末端插入。在中间插入需要元素逐个移动,但在现代芯片组上也可以很快执行。

< P> <代码> []/Cord>运算符是从普通数组继承的。它一直被理解为底层容器的快速(次线性时间)访问器。由于list is不支持次线性时间访问,因此它实现运算符是没有意义的

auto a = Container [10]; // Ideally I can assume this is quick
std::list::operator[]
等效的是
std::next
。记录于


这是索引容器的真正通用方法。如果容器支持随机访问,它将是常数时间,否则它将是线性的。

std::vector::insert
之所以实现,是因为
std::vector
必须满足的要求,而
操作符[]
不是任何概念(据我所知)所要求的,可能会添加到c++17中的
ContiguousContainer
概念中。因此,
operator[]
添加到可以像数组一样使用的容器中,而接口规范要求使用
insert
,因此满足特定概念的容器可以用于通用算法。

我认为Slava的答案做得最好,但我想提供一个补充说明。一般来说,对于数据结构,访问次数比插入次数要多,反之亦然。有许多数据结构可能会以插入为代价来优化访问,但相反的情况要难得多,因为它在现实生活中往往用处不大

运算符[]
,如果为链表实现,可能会访问现有值(类似于
向量的方式)<代码>插入
添加新值。如果后续的访问速度非常快,您很可能愿意在
向量中插入一些新元素,从而大大降低性能。在许多情况下,元素插入可能完全在关键路径之外,而关键路径由单个遍历或对现有数据的随机访问组成。因此,在这种情况下,让
insert
为您处理细节非常方便(实际上,高效、正确地编写代码有点烦人)。这实际上是向量的一种常见用法


另一方面,在链表上使用
操作符[]
几乎总是表明您使用了错误的数据结构。

这确实取决于“它同样低效”,但这不是可以假设的。“它同样低效”-高度推测性,完全取决于使用场景。你的问题可以很好地说明实际情况。对于
std::list::operator[]
,如果你真的愿意,你可以自己做;对于
std::vector::insert
,你不能。这两种方法都可以任意使用。因此,我认为没有必要针对某个具体案例来讨论它们。但对其中一个国家来说,实施被认为是不恰当的。为什么?@WhozCraig:你的断言毫无用处。你必须知道他们错了。你不能不知道他们是笨蛋。无论如何,对于其他读者来说:
vector::insert
和一个假设的
list::operator[]
通常在容器的大小n中具有O(n)复杂性。不过,索引操作符可以针对顺序访问进行优化。向量对原始数组直接索引的有效要求使得顺序插入优化可能不可能实现。如果您想在末尾插入,然后使用
push_-back
,您不需要
insert
,否则我没有领会您的意思?确实可以使用
push_-back
;类似于末尾插入的优化也适用。如果我们想在一行中插入多个元素,我们会这样做。我发现关于优化的评论有误导性,因为列表的索引运算符可以很容易地优化,而向量插入可能无法优化,由于矢量的作用是大块增长,所以我不太明白这个答案。中间插入总是O(n),恰好像<代码>列表::运算符[] < /代码>。末尾的插入速度更快,但插入的接口比这更通用,所以这与此无关。如果它可以“在许多平台上快速执行”,那么基本上说,尽管两个都是O(n),但是在向量的中间插入具有更好的常量,因此,如果你认为这是答案,那么这一点应该被强调。例如,
std::map
提供了具有对数复杂性的索引运算符,因此该参数不成立。还有,这个略图上的等价物(以及它是等价物的断言)是不正确的。如果
std::list
提供了索引,它很可能会针对顺序访问进行优化。−1“如果容器支持随机访问,它将是常数时间,否则它将是线性的。”这显然是错误的™.我不确定我是否同意DV。C++标准库不应该
auto a = *std::next (Container.begin (), 10); // This may take a little while