I recently started looking into allocators and the new pmr introduced in c++17.
Looking at the definition of std::pmr::new_delete_resouece on cppreference I read the following:
Returns a pointer to a memory_resource that uses the global operator new and operator delete to allocate memory.
That "global" is kinda confusing me. What does it mean? Does it just refers to the normal call of the operators like in
int* i = new int;
delete i;
thus allocating stuff on the heap, or does it refer to the static memory where global variables are allocated?
And what's the point in both cases to use such structure?
NicolBolas already gave a pretty clear answer, but since there appears to be some confusion in the comments regarding the "static storage duration" which I feels needs some clarity.
The function called new_delete_resource()
is defined, on cppreference, to have the following affect:
Return Value:
Returns a pointer p to a static storage duration object of a type derived from
std::pmr::memory_resource
, with the following properties:
- its
allocate()
function uses::operator new
to allocate memory;- its
deallocate()
function uses::operator delete
to deallocate memory;- for any
memory_resource
r
,p->is_equal(r)
returns&r == p
.The same value is returned every time this function is called.
(Emphasis mine)
What this means is that the std::pmr::memory_resource
object returned from this function has static-storage duration; not that calls of allocate()
operate on static storage duration.
For example, this may be implemented as:
namespace std::pmr {
memory_resource* new_delete_resource() {
static internal_new_delete_resource s_new_delete_resource;
return &s_new_delete_resource;
}
} // namespace std::pmr
To be clear: the following code does not allocate memory with static-storage duration (by default[1]):
auto* p = new_delete_resource()->allocate(...);
Rather, the above code is actually roughly equivalent to writing:
auto* p = new char[...];
The "global operators" that the documentation refers to are the functions ::operator new
and ::operator delete
.
[1] By default, these operate on the heap -- though ::operator new
and ::operator delete
can be overridden by the user if they choose to define these. A program is legally allowed to define their own allocation mechanism if they choose -- at which point this may, in fact, be static-storage duration. However, such a point is more esoteric; as far as the standard is concerned, the storage duration of the pointer is dynamic -- and not explicitly static storage.
Aside from being a suitable and useful default for an allocator (e.g. the default_resource
), this also offers great composability with other memory_resource
s. For example, a pool_resource
may use this as the upstream memory_resource
for when the pool runs out of allocations.
Having a resource like this becomes really important for symmetry with std::allocator<T>
(which means a cheap upgrade path), and for enabling std::pmr::polymorphic_allocator
to have a suitable default.
new
and delete
?new
and delete
are built-in facilities in C++ -- so it's easy to use these and base it in terms of this. Since the global ::operator new
and ::operator delete
can be overridden by custom definitions, this makes it a simple customization point that works seamlessly in any existing application. Additionally, it follows the existing pattern for std::allocator<T>
which used new
and delete
.
If, instead, this were std::malloc
/std::free
, or some other allocation function -- then old user code that previously defined custom hooks for ::operator new
would cease to function correctly. This would lead the behavior of a container using std::allocator<T>
to behave differently than a container using std::pmr::polymorphic_allocator<T>
with a std::pmr::new_delete_resource
-- which would be undesirable. (Note: the default resource is a new_delete_resource
, which is what provides this symmetry by default).