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