In some use case, you’ll need to allocate storage before creating objects inside this storage (a typical one is managing your objects inside pre-allocated pools of memory).
Then in order to create these objects, you may need to use placement new:
T *pobj = new(pstorage);
yet you have to provide a properly aligned pstorage
.
Many posts have been written on this topic but I’d like to summarize them in one place and to get clarification on some points.
Here are the different techniques I encountered.
(for instance but not only:
- Placement-new address alignment
- Properly allocate an array with placement new
)
storage for a single type
on the stack
// properly aligned stack storage to hold N contiguous T objects
alignas(T) unsigned char storage[N*sizeof(T)];
on the heap
I don’t get a correct solution. I was foolowing this idea:
// properly aligned heap storage to hold N contiguous T objects
unsigned char *storage = new unsigned char[N*sizeof(T)];
void * pstorage = std::align(alignof(T),N,storage,?)
may be properly aligned, because:
https://en.cppreference.com/w/cpp/language/new
“In addition, if the new expression is used to allocate an array of char, unsigned char, or std::byte(since C++17), it may request additional memory from the allocation function if necessary to guarantee correct alignment of objects of all types no larger than the requested array size, if one is later placed into the allocated array.”
It is unclear what amount of memory is actually obtained and how an aligned pointer can be obtained (I tried to use std::align
but what arguments can I give to it?).
either on stack or heap, by wrapping an aligned array
// in the spirit of std::aligned_storage
// can be on the stack or on the heap
template <typename T, std::size_t N>
struct Storage {
alignas(T) unsigned char storage[N * sizeof(T)];
}
Does this be usable in all situations?
Can I place objects in this buffer and then reinterpret_cast<T *>(storage)
without UB?
single storage for many objects of many types or resusable storage for different types
First we have to get a storage, whatever its alignment
on the stack
// storage on the stack, no requirement on alignment
unsigned char storage[N];
on the heap
// storage on the heap, no requirement on alignment
unsigned char *storage = new unsigned char[N*sizeof(T)];
getting an aligned pointer
// let p a pointer to a byte inside storage, originaly set to storage
// issue storage+N-p is std::ptrdiff_t not std::size_t
// remaining storage should probably be kept up to date manually into a dedicated variable // properly aligned storage to hold M contiguous T objects or nullptr std::align(alignas(T),M,p,storage+N-p);
// or (using a dedicated variable)
std::align(alignas(T),M,p,remaining_space);
// major issue, the required remaining_space cannot be known in advance AFAIU, it may lead to expansive reallocation(s) and copy(ies)
Until recently I thought that you could do without std::align
by doing arithmetic on the pointer value representation (after casting, for instance, to std::uintptr_t
when its available) in order to check or adjust alignment and to compute space requirements. But, from the standard:
The value representation of pointer types is implementation-defined
Only pointer arithmetic, inside arrays, is defined, in an abstract way.
But, using std::align
rises an issue on the required storage size.
Isn’t there a portable and legal way to estimate, in advance, the space requirement for a storage of heterogeneous objects?
NB on the “reuse” case
Before reusing a storage (or a portion of it) you’ll have to destroy all non-trivially destructible objects that were previously there.
placement new: array form
I can’t find a way to use placement new
for arrays in a correct way. I found (and wrote) several post on the subject and I was left with the impression that it’s not usable (from memory, one reason was that you could not destroy easily the array).
The solution would be to place the object manually (and destroy them in the same way).
But in this case, is it legal to alias an array on the address of the first object? Can we perform pointer arithmetic on this “fake array”?
Saying it in another way: would it be legal to reinterpret_cast
the pointer to the first object to an plain array of objects (possibly to a std::array<T,M>*
)?
Isn’t there a cleaner, simpler solution?
I’d like the appropriate wording from the standard (I think it might relate to implicit lifetime).
final word on std::max_align_t
AFAIU it gives a suitable alignment for “scalar types” but it’s not the same as “all types”. When can std::max_align_t
be used and how?