According to cppref:
std::allocator<T>::allocate_at_least
Allocates
count * sizeof(T)
bytes of uninitialized storage, wherecount
is an unspecified integer value not less thann
, by calling::operator new
(an additionalstd::align_val_t
argument might be provided), but it is unspecified when and how this function is called.Then, this function creates an array of type
T[count]
in the storage and starts its lifetime, but does not start lifetime of any of its elements.
However, I think the already existing std::allocator<T>::allocate
can do the same thing.
Why do we need std::allocator<T>::allocate_at_least
in C++23?
std::allocator is the default memory allocator for the standard library containers, and you can substitute your own allocators. This allows you to control how the standard containers allocate memory.
allocator is the memory allocator for the STL containers. This container can separate the memory allocation and de-allocation from the initialization and destruction of their elements. Therefore, a call of vec. reserve(n) of a vector vec allocates only memory for at least n elements.
allocate
may allocate more elements than were requested but it has no way to return to its caller the actual allocated size.
This is the purpose of allocate_at_least
, it's implementation might be the same as allocate
and might allocate exactly the same number of elements, the difference is that it is able to return the number of elements allocated to the caller which means the caller can make use of those extra elements if necessary.
allocate_at_least
does not do the same thing as allocate
. Compare (allocate
):
Allocates
n * sizeof(T)
bytes of uninitialized storage...
with (allocate_at_least
):
Allocates
count * sizeof(T)
bytes of uninitialized storage, wherecount
is an unspecified integer value not less thann
...
Moreover, allocate
returns:
Pointer to the first element of an array of
n
objects of typeT
...
While allocate_at_least
returns:
std::allocation_result<T*>{p, count}
, wherep
points to the first element of an array ofcount
objects of typeT
...
The caller thus gets the information about the actually allocated size.
The motivation can be found in 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.
This comes from notes of cppref:
allocate_at_least is mainly provided for contiguous containers, e.g. std::vector and std::basic_string, in order to reduce reallocation by making their capacity match the actually allocated size when possible.
The "unspecified when and how" wording makes it possible to combine or optimize away heap allocations made by the standard library containers, even though such optimizations are disallowed for direct calls to ::operator new. For example, this is implemented by libc++.
After calling allocate_at_least and before construction of elements, pointer arithmethic of T* is well-defined within the allocated array, but the behavior is undefined if elements are accessed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With