sizeof(std::list<T>) 可以因不同类型的 T 而异吗?

Can sizeof(std::list<T>) vary for different types of T?

本文关键字:同类型 list std lt gt sizeof      更新时间:2023-10-16

我可以假设对于任何类型的T,类型std::list<T>都将具有相同的常量大小吗?为了说明清楚,我指的只是"主"类型本身的大小,而不是它分配的内存的大小

在我看来,假设T本身的大小应该只影响使用分配器分配的列表节点的大小似乎是合乎逻辑的。

然而,有两件事可能会导致sizeof(std::list<T>)的变异,我可以想到:

  1. 一个标准C++库,试图通过将一些T实例放入std::list<T>本身来"优化"std::list类型。对我来说,这似乎是个坏主意,它可能打破了标准提出的"恒定时间"要求
  2. 一种标准C++,对某些类型具有std::list<T>的库专用化,专用化具有不同的大小

我想不出(1)或(2)有什么用,但我可能错了。

我想实现的是为内部使用std::list<T>的模板类使用一个固定大小的切片分配器。


注意:这是关于不同类型的T的差异,而对于不同的分配器,不是。我将对所有实例化进行显式控制,并且它们都将使用std::allocator

,可以。

据我所知,该标准并不能精确保证list<T>对象的大小。不过,还有其他保证。


让我们揭穿一些事情:

一个标准C++库,试图通过将一些T实例放入std::list<T>本身来"优化"std::list类型。对我来说,这似乎是个坏主意,它可能打破了标准提出的"恒定时间"要求;

它不会以任何方式打破"恒定时间"的要求,只要这个数字是有界的,因为O(5)是O(1)。但是,在splice操作期间,节点应该从一个列表移动到另一个,而不在内存中移动。因此,禁止本地存储。

一种标准C++,对某些类型具有std::list<T>的库专用化,专用化具有不同的大小。

由于我们已经排除了本地存储的可能性,因此很难想象基础std::list<T>类型本身有任何其他变体


让我们担心什么是重要的:

std::list<T>分配的内存由其第二个模板参数提供:分配器。大多数分配器(包括默认的std::allocator<T>)使用一个简单的指针类型:T*。。。但是,分配器应该可以随意更改这种类型。

因此,通过更改allocator参数(更确切地说,是其指针类型),在我所见过的所有实现中,人们自然会更改std::list容器的大小

请注意,分配器本身可以专门用于某些类型,但使用rebind魔术会更难实现

C++11标准(Containers)的第23章没有说明它们的大小,因此从技术上讲,您不能假设大小是恒定的。实现者可以(理论上)以影响其占用空间的方式为特定原语专门化某个容器。

在实践中,您可以使用静态断言sizeof(list<T>) == sizeof(list<int>),并使用sizeof(list<int>)作为"固定"大小。可能发生的最糟糕的事情是编译时错误。

使用std::list<T>的类的固定大小切片分配器没有什么意义,除非您也为列表本身使用固定大小的切片分配器,否则列表项的分配可能比列表头的分配多得多。

现在,您当然可以使用列表的第二个模板参数分配器为列表提供自己的固定大小切片分配器。然而,有一个转折点:你不知道切片应该有多大。list<T>接受allocator<T>,但它真正需要的是allocator<U>,其中U是一个包含上一个/下一个指针和t的结构。为了得到它,它调用了具有签名template <typename T> template <typename T> allocator<U> allocator<T>::rebind<U>()的方法rebind

由于我实际上需要做一次,所以我所做的就是创建一个对象,该对象包含多个切片大小的分配器集合。分配器本身拥有一个指向此的共享指针和一个指向分配器的共享指针,以确定其参数的大小。在重新绑定时,我从集合中提取了适当的分配器,或者在不存在分配器的情况下创建一个分配器。如果您这样做,它将作为副作用解决列表的分配,因为如果有多个大小,它将为每个创建单独的池。而且不会有很多不同的尺寸,因为物体不会很大。