2

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?

Mdp11
  • 552
  • 4
  • 13
  • See this SO answer https://stackoverflow.com/a/38011239/8339821. Your question is a duplicate. – user14063792468 Feb 03 '21 at 17:56
  • Does this answer your question? [polymorphic\_allocator: when and why should I use it?](https://stackoverflow.com/questions/38010544/polymorphic-allocator-when-and-why-should-i-use-it) – user14063792468 Feb 03 '21 at 17:56
  • 1
    Thanks, but I already looked into that answer that surely helped me getting a wide concept of pmr in general. But here I'm more specifically talking about std::pmr::new_delete_resource – Mdp11 Feb 03 '21 at 18:01
  • I don't have the time to make this a proper answer, but `new_delete_resource` is exactly as you suspect; it uses `new` and `delete` for memory. Why? Because it's a simple default and uses C++'s built-in facilities for allocations, much like `std::allocator`. This is just _a_ resource; there are plenty of more specific/optimized ones available, like pools or monotonic resources (which may also use `new_delete_resource` as an upstream resource for more memory) – Human-Compiler Feb 03 '21 at 18:04
  • Your question shoud be: what does `global` `operator` means in `C++`, and what does `static memory` means in `C++`. The `std::pmr::new_delete_resource` web page is clear about all that. – user14063792468 Feb 03 '21 at 18:18
  • @Human-Compiler thanks for your answer, but if it would just use new and delete memory would be allocated on the heap, instead as another user answered, on cppreference there's written to the pointer returned is to a static storage duration object. So they are used in a different context – Mdp11 Feb 03 '21 at 18:28
  • 1
    @Mdp11 NicolBolas already answered this quite well; `new` is referring to the global [`operator new`](https://en.cppreference.com/w/cpp/memory/new/operator_new). `new` **does not** return anything with static storage duration at all unless someone replaces the global operator. The static storage duration you are referring to is for the pointer returned from the _function `new_delete_resource()` -- not from the allocation calls that come from that returned object. – Human-Compiler Feb 03 '21 at 18:57

3 Answers3

5

operator new/delete can be overridden for specific types, so that if you invoke new T, the system will call the memory allocator function specifically associated with T.

However, the low-level memory allocation functions still exist. These functions are global functions. So when that text says that it's calling the "global operator new", it means that literally: the operator new function which is global, not specific to a particular type.

This function can also be user-provided, but that would affect all operations that attempt to call it.


And what's the point in both cases to use such structure?

The point is... you want to use the global memory allocation functions to allocate memory for a container allocator. That's what std::allocator does, after all.

PMR memory resources are a way to allow the allocation mechanism to not be part of a container's type. So any vector<T, pmr::polymorphic_allocator> could use any particular pmr::memory_resource-derived class to allocate its memory.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
2

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.

So what's the point?

Aside from being a suitable and useful default for an allocator (e.g. the default_resource), this also offers great composability with other memory_resources. 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.

Why 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).

Human-Compiler
  • 11,022
  • 1
  • 32
  • 59
-8

From the same resource you mentioned:

Returns a pointer p to a static storage duration object of a type derived from std::pmr::memory_resource...

And what does static storage do:

static storage duration. The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern. See Non-local variables and Static local variables for details on initialization of objects with this storage duration.

So as per comment, it calls a function(operator in this particular case), that returns static storage duration object.

I understand the above as such, that it calls some operator new that returns static storage object. The operator new is global in the normal sense. So this is not some new specific to some struct/union/class.

user14063792468
  • 839
  • 12
  • 28
  • 1
    You're confusing the object returned by `new_delete_resource` with what the `new_delete_resource` allocation functions do. The OP asked about the latter. – Nicol Bolas Feb 03 '21 at 18:25
  • @NicolBolas No, I am not. – user14063792468 Feb 03 '21 at 18:46
  • @NicolBolas And your answer is just another wording for what I've posted in my answer. – user14063792468 Feb 03 '21 at 18:48
  • 2
    `new` is not what is returning pointers with static storage duration. The function [`new_delete_resource()`](https://en.cppreference.com/w/cpp/memory/new_delete_resource) is what returns a pointer to a `std::pmr::memory_resource`, which has static storage duration -- which is completely different. The pointers returned from the _allocation functions_ come from the global `new` operator -- which is never static storage duration unless someone defines a custom global `operator new`. The second part of your answer is wrong. – Human-Compiler Feb 03 '21 at 19:00
  • @Human-Compiler I did not understand your comment. Why do you write all this to me? How do my post relate to your comment? Please explain. My plan for today did not include to listen what function returns what. It is OP who asks. – user14063792468 Feb 03 '21 at 19:48
  • 2
    @user14063792468: He's pointing out the same thing I did: that the thing with "static storage duration" is the return value of `new_delete_resource()`, ***NOT*** what the "global operator new/delete" return, which is what your answer implies. – Nicol Bolas Feb 03 '21 at 20:05
  • @NicolBolas Quote starts: ...that uses the global operator new... That "global" is kinda confusing me. What does it mean? Quote ends. That is the first part of a question. That is what he asked. – user14063792468 Feb 03 '21 at 21:41
  • 1
    @user14063792468: And you never explained what the "global operator new" is or what it means. – Nicol Bolas Feb 03 '21 at 21:43
  • @NicolBolas Quote: The operator new is global in the normal sense... Anyway, If you and OP do not care to read, I finish with this topic. – user14063792468 Feb 03 '21 at 22:27