当 T 是原始类型时, std::vector<T>::clear() 的复杂性是多少?

Posted

技术标签:

【中文标题】当 T 是原始类型时, std::vector<T>::clear() 的复杂性是多少?【英文标题】:What is the complexity of std::vector<T>::clear() when T is a primitive type? 【发布时间】:2012-06-27 23:03:11 【问题描述】:

我知道 clear() 操作的复杂性与容器的大小成线性关系,因为必须调用析构函数。但是原始类型(和 POD)呢?似乎最好的办法是将向量大小设置为 0,以便复杂度保持不变。

如果可以的话,std::unordered_map 也可以吗?

【问题讨论】:

【参考方案1】:

似乎最好的办法是将向量大小设置为 0,这样复杂度就恒定了。

一般来说,resizing a vector to zero 的复杂度与vector 中当前存储的元素数量呈线性关系。因此,将vector 的大小设置为零比调用clear() 没有任何优势——两者本质上是相同的。

但是,至少有一种实现(libstdc++,bits/stl_vector.h 中的源代码)通过使用部分模板特化为原始类型提供了 O(1) 复杂度。

clear() 的实现导航到bits/stl_construct.h 中的std::_Destroy(from, to) 函数,该函数执行非平凡的编译时优化:它声明了一个辅助模板类_Destroy_aux,模板参数类型为@ 987654335@。该类对true 有一个部分特化,对false 有一个显式特化。两种特化都定义了一个名为__destroy 的静态函数。如果模板参数为true,则函数体为空;如果参数是false,则调用body contains a loop invoking T's destructorstd::_Destroy(ptr)

诀窍来了line 136:

std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::
__destroy(__first, __last);

辅助类是根据__has_trivial_destructor 检查的结果实例化的。检查器为内置类型返回true,为具有非平凡析构函数的类型返回false。因此,对 __destroy 的调用对于 intdouble 和其他 POD 类型将变为无操作。

std::unordered_mapvector 的不同之处在于它可能需要删除代表 POD 对象的“哈希桶”的结构,而不是删除对象本身*clearO(1) 的优化是可能的,但是很大程度上依赖于实现,所以我不会指望它。


* 确切答案取决于实现:基于开放寻址(线性探测、二次探测等)实现collision resolution 的哈希表可能能够删除O(1) 中的所有桶;但是,基于单独链接的实现必须一个接一个地删除存储桶。

【讨论】:

这对resize来说是真的吗? @Quimby 是的。 clear 重用了向下调整向量大小的逻辑。【参考方案2】:

gcc 的std::_Destroy 版本,clear() 最终使用的版本,尝试对类型是否具有微不足道的析构函数进行模板化,因此在这种情况下,即使没有优化通过,复杂性也是恒定的。但是我不知道模板的效果如何。

【讨论】:

以上是关于当 T 是原始类型时, std::vector<T>::clear() 的复杂性是多少?的主要内容,如果未能解决你的问题,请参考以下文章

std::vector 插入是如何实现的? C++

std::vector<std::array<T, N>> 或 std::array<std::vector<T>,N> 类型的数组如何存储在内存中?

迭代时在 std::vector 中保存指向不同类型的指针

调整 std::vector<std::unique_ptr<T>> 大小的性能

std::vector 或 std::list 用于 std::unordered_map 存储桶?

继承 std::vector 时出错