allocate_delete const; delete unnecessary null check from allocate_new; add some more design questions.allocation_deleter. Add formal wording.Short form: We propose to add library functions that allow the systematic use of allocators as a customisation point for dynamic allocations. The new functions complete the following picture:
using operator {new,delete} | using allocator | |
|---|---|---|
| Manual | T * p = new T(args...) |
auto p = allocator_new<T>(alloc, args...) |
delete p |
allocator_delete(alloc, p) |
|
| Unique pointer | default_delete<T> |
allocation_deleter<T> |
make_unique<T>(args...) |
allocate_unique<T>(alloc, args...) |
|
| Shared pointer | make_shared<T>(args...) |
allocate_shared<T>(alloc, args...) |
Long form: The standard library rarely uses
new/delete directly, but instead allows customisation of dynamic
allocation and object construction via allocators. Currently this customisation is only
available for container elements and for shared_ptr (as well as for
a few other types that require dynamically allocated memory), but not for the top-level
objects themselves.
The proposal is to complete the library facilities for
allocator-based customisation by providing a direct mechanism for creating and destroying
a dynamically stored object through an allocator, as well as a new deleter type for
unique_ptr to hold an allocator-managed unique pointee, together with the
appropriate factory function.
std::allocate_unique<std::vector<T,
ScopedArenaAlloc<T>>>(arena_alloc) allows this. (Here we assume the
use of the usual alias idiom template <class T> using ScopedArenaAlloc =
std::scoped_allocator_adaptor<ArenaAlloc<T>>;).auto p =
std::allocator_new<std::vector<T,
ScopedShmemAlloc<T>>>(shmem_alloc). The returned pointer is
presumably an offset pointer, and its offset value needs to be communicated to the other
participating processes. The last process to use the container calls
allocator_delete(shmem_alloc, p).To allow std::unique_ptr to use custom allocators, we first need a deleter template that stores the allocator:
The factory function is:
The type T must not be an array type.
noexcept).In 20.9.2, add to the synopsis:
Insert a new subsection between 20.9.8 (allocator traits) and 20.9.9 (the default allocator):
The function templates allocator_new and allocator_delete
create and delete objects dynamically using an allocator to provide storage and perform
construction (rather than by looking up an allocation function (3.7.4.1)).
template <class T, class A, class ...Args>
typename allocator_traits<A>::pointer
allocator_new(A& alloc, Args&&... args);
Requires: A shall be an allocator class for type T.
Effects: Obtains storage for one element from alloc
and constructs an object of type T from args using
alloc. If the construction exits with an exception, the storage
is released using alloc.
Returns: A pointer to the obtained storage that holds the constructed object. [Note: This pointer may have a user-defined type. – end note]
Throws: Any exception thrown by the constructor of T.
template <class A>
void allocator_delete(A& alloc, typename allocator_traits<A>::const_pointer p);
Requires: p was obtained from an allocator that compares
equal to alloc, and p is dereferenceable.
Effects: Uses alloc to destroy the object *p
and to release the underlying storage.
Insert a new subsection 20.10.1.1.? after 20.10.1.1.3 (default_delete<T[]>):
allocation_deleter<A> [unique.ptr.dltr.alloc]allocation_deleter(const A& alloc);
Effects: Initializes a_ with alloc.
template <class B> allocation_deleter(const allocation_deleter<B>& other);
Effects: Initializes a_ with other.a_.
Remarks: This constructor shall not participate in overload resolution unless
typename allocator_traits<b>::pointer is implicitly convertible to pointer.
void operator()(pointer p) const;
Effects: Calls allocator_delete(a_, p).
Append a new paragraph to the end of subsection 20.10.1.4 (unique_ptr creation):
template <class T, class A, class ...Args>
unique_ptr<T, allocation_deleter<A>>
allocate_unique(A& alloc, Args&&... args)
Remarks: This function shall not participate in overload resolution unless
T is not an array.
Returns: unique_ptr<T, allocator_delete<A>>(allocator_new<T>(alloc, std::forward<Args>(args)...), alloc).