为什么std :: list.size()不是常量时间?

时间:2012-10-31 11:45:05

标签: c++ performance list size

此代码运行0.012秒:

 std::list<int> list;
 list.resize(100);
 int size;
 for(int i = 0 ; i < 10000; i++)
     size = list.size();

这个为9.378秒:

 std::list<int> list;
 list.resize(100000);
 int size;
 for(int i = 0 ; i < 10000; i++)
     size = list.size();

在我看来,有可能以这种方式实现std :: list,该大小将存储在私有变量中,但根据这个,每次调用size时都会再次计算。任何人都可以解释原因吗?

2 个答案:

答案 0 :(得分:16)

常量时间size()和常量时间list.splice之间存在冲突。委员会选择赞成splice

当您在两个列表之间拼接节点时,您必须计算移动的节点以更新两个列表的大小。通过改变一些内部指针,它消除了拼接节点的许多优点。


正如评论中所指出的,C ++ 11通过放弃O(1)来改变这一点,因为splice的一些罕见(?)用途:

void splice(const_iterator position, list& x, const_iterator first, const_iterator last);
void splice(const_iterator position, list&& x, const_iterator first, const_iterator last);
  

复杂性&x == this时的固定时间;否则,线性时间。

答案 1 :(得分:9)

ISO/IEC 14882:2011,§C.2.12,第23条:“容器库”:

  

更改:size()成员函数的复杂性现在不变

     

理由:缺乏size()复杂性的规范导致实现不同,性能特征不一致。

     

对原始功能的影响:符合C ++ 2003的某些容器实现可能不符合本国际标准中指定的size()要求。将容器(如std :: list)调整到更严格的要求可能需要进行不兼容的更改。


评论:

在23.3.5.5 - “列表操作”中,再次在ISO/IEC 14882:2011中:

  

list提供了三个拼接操作,可以破坏性地将元素从一个列表移动到另一个列表。如果get_allocator()!= x.get_allocator(),则拼接操作的行为是不确定的。

     

void splice(const_iterator position,list&amp; x);
  void splice(const_iterator position,list&amp;&amp; x);
  需要:&amp; x!=此。
  效果:在位置前插入x的内容,x变为空。指针和x的移动元素的引用现在指的是那些相同的元素,但作为* this的成员。引用移动元素的迭代器将继续引用它们的元素,但它们现在表现为迭代器为* this,而不是x。   复杂性:恒定时间。

     

void splice(const_iterator position,list&amp; x,const_iterator i);
  void splice(const_iterator position,list&amp;&amp; x,const_iterator i);
  效果:在列表x之前插入i指向的元素,然后从x中删除元素。如果position == i或position == ++ i,结果不会改变。指针和对* i的引用继续引用相同的元素,但作为* this的成员。 I i(包括i本身)的迭代器继续引用相同的元素,但现在表现为迭代器为* this,而不是x。   需要:我是x的有效可解引用迭代器   复杂性:恒定时间。

     

void splice(const_iterator position,list&amp; x,const_iterator first,const_iterator last);
  void splice(const_iterator position,list&amp;&amp; x,const_iterator first,const_iterator last);
  效果:在位置之前插入[first,last]范围内的元素,并从x中删除元素。   需要:[first,last]是x中的有效范围。如果position是[first,last]范围内的迭代器,则结果是未定义的。指针和x的移动元素的引用现在指的是那些相同的元素,但作为* this的成员。引用移动元素的迭代器将继续引用它们的元素,但它们现在表现为迭代器为* this,而不是x。   复杂性:如果&amp; x == this,则为恒定时间;否则,线性时间。