为什么在C++23中要使用allocate_at_least()?

16
根据cppref

std::allocator<T>::allocate_at_least

通过调用::operator new(还可以提供一个额外的std::align_val_t参数),分配至少n个未初始化的存储空间,每个存储空间的大小为count * sizeof(T)字节,其中count是一个未指定的整数值,但不小于n

然后,该函数在该存储空间中创建T[count]类型的数组,并开始其生命周期,但不会开始任何元素的生命周期。

然而,我认为已经存在的std::allocator<T>::allocate可以完成同样的任务。 为什么我们需要C++23中的std::allocator<T>::allocate_at_least

我不确定,但我认为这是为了提高内存分配和释放的效率。因为现在内存不需要完全等于请求的大小,它可以成为主内存页面大小的倍数(我猜?)。所以在这些特定大小中分配或释放内存将更容易。 - Afshin
至少这个返回实际大小。虽然我不确定它存在的主要原因是什么。 - chris
4个回答

20

allocate 函数可能会分配比请求更多的元素,但它无法向调用者返回实际分配的大小。

allocate_at_least 的目的就是为了解决这个问题。它的实现可能与allocate相同,并且可能只分配完全相同数量的元素,不同之处在于它能够将分配给调用者的元素数返回给调用者,这意味着调用者可以使用那些额外的元素。


15
allocate_at_leastallocate不是完全相同的功能。对比(allocate):分配n * sizeof(T)字节的未初始化存储空间...,与(allocate_at_least):分配至少n个未指定整数值的count * sizeof(T)字节的未初始化存储空间... 此外,allocate返回:指向类型T数组的前n个对象的第一个元素的指针... 而allocate_at_least返回:std::allocation_result<T*>{p, count},其中p指向数组中count个类型为T的对象的第一个元素... 因此,调用者可以获得有关实际分配大小的信息。其动机可以在P0401R6; Section Motivation中找到。

Consider code adding elements to vector:

std::vector<int> v = {1, 2, 3};
// Expected: v.capacity() == 3

// Add an additional element, triggering a reallocation.
v.push_back(4);

Many allocators only allocate in fixed-sized chunks of memory, rounding up requests. Our underlying heap allocator received a request for 12 bytes (3 * sizeof(int)) while constructing v. For several implementations, this request is turned into a 16 byte region.


1
“allocate” 从 ::operator new 获取其存储空间,该操作允许进行超额分配。鉴于此,答案并未真正解释实际差异。 - chris
1
@chris 好的,你说得对,我已经相应地更新了答案。 - Daniel Langr

3
这来自于cppref的笔记:

allocate_at_least主要提供给连续的容器,例如std::vector和std::basic_string,以便在可能时匹配其实际分配大小从而减少重分配。

“未指定何时和如何”的措辞使得可以组合或优化标准库容器所做的堆分配,即使这种优化不允许对::operator new的直接调用。例如,libc++就是这样实现的。

在调用allocate_at_least之后,在元素构造之前,T*的指针算术在已分配的数组内是定义良好的,但如果访问元素,则行为是未定义的。


你的第二个强调部分并不是只适用于 allocate_at_least。例如,相同的注释也适用于 allocate。你也可以将 int main() { int* p = new int[5]; delete[] p; } 放入 Clang 中,它应该也会省略它。 - chris

1
使用现代内存分配器(如mimalloc、jemalloc或TCMalloc),分配的内存大小是以2的幂次分配的,直到达到固定限制。超过该限制的大小将以页面的倍数进行分配。在这两种情况下,实际分配的内存可能会更大。allocation_result返回实际分配的内存大小,以便使用C++23分配器的容器的容量可能会比预期的更大,并且在此之后可能会减少重新分配的次数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接