【问题标题】:Can sizeof(std::list<T>) vary for different types of T?sizeof(std::list<T>) 可以因不同类型的 T 而有所不同吗?
【发布时间】:2012-08-01 10:16:14
【问题描述】:

我可以假设对于任何类型T,类型std::list&lt;T&gt; 将具有相同的恒定大小吗?为了清楚起见,我的意思是“主”类型本身的大小,而不是它分配的内存。

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

但是,有两件事可能会导致sizeof(std::list&lt;T&gt;) 的变化我能想到:

  1. 一个标准 C++ 库试图通过将一些 T 实例放入 std::list&lt;T&gt; 本身来“优化”std::list 类型。这对我来说似乎是个坏主意,它可能违反了标准提出的“恒定时间”要求;
  2. 一种标准 C++,对于某些类型具有 std::list&lt;T&gt; 的库特化,并且特化具有​​不同的大小。

我想不出 (1) 或 (2) 的任何用途,但我可能错了。

我正在考虑实现的是使用固定大小的切片分配器来使用std::list&lt;T&gt; 内部的模板类。


注意:这是关于不同类型的T 的差异,不是的不同分配器。我将明确控制所有实例化,它们都将使用std::allocator

【问题讨论】:

  • 有趣,+1。我很确定,标准不保证相同的大小,但仍然 +1 并且只是评论:)
  • 我想到了vector而不是list,但它可能包含T*,而且并非所有指针的大小都相同...好吧,我不完全确定您将如何创建成员函数的容器,但是 ...
  • @BoBTFish - 所有指针大小相同。
  • @KirilKirov:不,那不是真的。
  • @KirilKirov:PDP-10 要求 char* 大于 int*,因为它需要对字地址和 36 位字中的字节偏移进行编码。这是在以前只支持字寻址的架构中添加字节寻址的结果。

标签: c++ stl


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    是的,可以。

    据我所知,该标准并未精确保证list&lt;T&gt; 对象的大小。不过,还有其他保证。


    让我们揭穿一些事情:

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

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

    一种标准 C++,对于某些类型具有 std::list&lt;T&gt; 的库特化,并且特化具有​​不同的大小。

    由于我们已经排除了本地存储的可能性,很难想象基本std::list&lt;T&gt; 类型的任何其他变体本身


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

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

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

    请注意,分配器本身可以专门用于某些类型,但是使用重新绑定魔法有点难以实现。

    【讨论】:

    • +1 用于专门考虑分配器如何影响足迹。非常好。
    【解决方案3】:

    使用std::list&lt;T&gt; 的类的固定大小的切片分配器几乎没有意义,除非您还为列表本身使用固定大小的切片分配器,否则列表项的分配可能比列表标题的分配更多。

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

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

    【讨论】:

    • 列表在切片分配器内部使用,因此必须直接分配。但是,std::list&lt;T&gt; 本身应该足够小,以便从使用切片分配器中受益。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 2015-09-27
    • 1970-01-01
    相关资源
    最近更新 更多