简体   繁体   English

std :: vector *在增加容量时是否有*移动对象?或者,分配器可以“重新分配”吗?

[英]Does std::vector *have* to move objects when growing capacity? Or, can allocators “reallocate”?

A different question inspired the following thought: 一个不同的问题启发了以下思想:

Does std::vector<T> have to move all the elements when it increases its capacity? std::vector<T>在增加容量时是否必须移动所有元素?

As far as I understand, the standard behaviour is for the underlying allocator to request an entire chunk of the new size, then move all the old elements over, then destroy the old elements and then deallocate the old memory. 据我所知,标准行为是底层分配器请求新大小的整个块,然后移动所有旧元素,然后销毁旧元素,然后解除分配旧内存。

This behaviour appears to be the only possible correct solution given the standard allocator interface. 在给定标准分配器接口的情况下,此行为似乎是唯一可能的正确解决方案。 But I was wondering, would it make sense to amend the allocator to offer a reallocate(std::size_t) function which would return a pair<pointer, bool> and could map to the underlying realloc() ? 但我想知道,修改分配器以提供reallocate(std::size_t)函数是否有意义,该函数将返回一pair<pointer, bool>并且可以映射到底层的realloc() The advantage of this would be that in the event that the OS can actually just extend the allocated memory, then no moving would have to happen at all. 这样做的好处是,如果操作系统实际上只能扩展分配的内存,那么根本不需要进行任何移动。 The boolean would indicate whether the memory has moved. 布尔值表示内存是否已移动。

( std::realloc() is maybe not the best choice, because we don't need do copy data if we cannot extend. So in fact we'd rather want something like extend_or_malloc_new() . Edit: Perhaps a is_pod -trait-based specialization would allow us to use the actual realloc , including its bitwise copy. Just not in general.) std::realloc()可能不是最佳选择,因为如果我们不能扩展,我们不需要复制数据。所以实际上我们更想要像extend_or_malloc_new()这样的东西。 编辑:也许是一个is_pod -trait-基于特殊化将允许我们使用实际的realloc ,包括它的按位复制。只是不一般。)

It seems like a missed opportunity. 这似乎错过了机会。 Worst case, you could always implement reallocate(size_t n) as return make_pair(allocate(n), true); 最糟糕的情况是,你总是可以实现reallocate(size_t n)作为return make_pair(allocate(n), true); , so there wouldn't be any penalty. ,所以不会有任何处罚。

Is there any problem that makes this feature inappropriate or undesirable for C++? 是否有任何问题导致此功能对C ++不合适或不合适?

Perhaps the only container that could take advantage of this is std::vector , but then again that's a fairly useful container. 也许唯一可以利用它的容器是std::vector ,但是这又是一个非常有用的容器。


Update: A little example to clarify. 更新:澄清一个小例子。 Current resize() : 当前resize()

pointer p = alloc.allocate(new_size);

for (size_t i = 0; i != old_size; ++i)
{
  alloc.construct(p + i, T(std::move(buf[i])))
  alloc.destroy(buf[i]);
}
for (size_t i = old_size; i < new_size; ++i)
{
  alloc.construct(p + i, T());
}

alloc.deallocate(buf);
buf = p;

New implementation: 新实施:

pair<pointer, bool> pp = alloc.reallocate(buf, new_size);

if (pp.second) { /* as before */ }
else           { /* only construct new elements */ }

When std::vector<T> runs out of capacity it has to allocate a new block. std::vector<T>耗尽容量时,它必须分配一个新块。 You have correctly covered the reasons. 你已经正确地说明了原因。

IMO it would make sense to augment the allocator interface. 国际海事组织是有意义的增加分配器接口。 Two of us tried to for C++11 and we were unable to gain support for it: [1] [2] 我们两个人试图使用C ++ 11而我们无法获得支持: [1] [2]

I became convinced that in order to make this work, an additional C-level API would be needed. 我开始相信,为了使这项工作,需要一个额外的C级API。 I failed in gaining support for that as well: [3] 我也未能获得支持: [3]

In most cases, realloc will not extend the memory, but rather allocate a separate block and move the contents. 在大多数情况下, realloc不会扩展内存,而是分配一个单独的块并移动内容。 That was considered when defining C++ in the first place, and it was decided that the current interface is simpler and not less efficient in the common case. 在首先定义C ++时考虑了这一点,并且在常见情况下确定当前接口更简单且效率不高。

In real life, there are actually few cases where realloc is able to grow . 在现实生活中,实际上很少有realloc能够增长的情况 In any implementation where malloc has different pool sizes, chances are that the new size (remember that vector sizes must grow geometrically) will fall in a different pool. malloc具有不同池大小的任何实现中,新大小(记住vector大小必须几何增长)可能会落入不同的池中。 Even in the case of large chunks that are not allocated from any memory pool, it will only be able to grow if the virtual addresses of the larger size are free. 即使在没有从任何内存池分配的大块的情况下,如果较大的虚拟地址是空闲的,它也只能增长。

Note that while realloc can sometimes grow the memory without moving , but by the time realloc completes it might have already moved (bitwise move) the memory, and that binary move will cause undefined behavior for all non-POD types. 请注意,虽然realloc有时可以在不移动的情况下增加内存,但是在realloc完成时它可能已经移动 (按位移动)内存,并且该二进制移动将导致所有非POD类型的未定义行为。 I don't know of any allocator implementation (POSIX, *NIX, Windows) where you can ask the system whether it will be able to grow , but that would fail if it requires moving . 我不知道任何分配器实现(POSIX,* NIX,Windows),您可以询问系统是否能够增长 ,但如果它需要移动则会失败。

Yep, you're right that the standard allocator interface doesn't provide optimizations for memcpy'able types. 是的,你是对的,标准的分配器接口不能为memcpy'able类型提供优化。

It's been possible to determine whether a type can be memcpy'd using boost type traits library (not sure if they provide it out of the box or one would have to build a composite type discriminator based on the boost ones). 可以确定是否可以使用boost类型特征库来记忆类型(不确定它们是否开箱即用,或者是否必须基于增强型构建复合类型鉴别器)。

Anyway, to take advantage of realloc() one would probably create a new container type that can explicitly take advantage of this optimization. 无论如何,为了利用realloc() ,可能会创建一个可以明确利用此优化的新容器类型。 With current standard allocator interface it doesn't seem to be possible. 使用当前的标准分配器接口似乎不可能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM