Given an existing type T
, it is possible to overalign it on the stack with the alignas()
keyword:
alignas(1024) T variable;
For dynamic allocation, we have a cumbersome syntax :
T *variable = new (std::align_val_t(1024)) T;
However, there are two problems with this syntax:
A workaround seems would be the definition of a new type that encapsulates T
:
alignas(1024)
struct AlignedType {
T _;
};
Alignedtype variable = new AlignedType; // Properly aligned in c++17
delete variable; // Suitable aligned deallocation function called in c++17
This workaround is messy, if T
's constructor has parameters, we must add some syntaxic shenanigan to forward constructor arguments, and we need to access variable._
to get the real content.
Inheritance is a bit simpler, but if T
is a fundamental type (like uint32_t
), we can't use inheritance.
My question is as follow:
Does something like
using AlignedType = alignas(32) T;
is possible (the above does not compile) or is it just not possible to dynamically allocate an existing type using custom alignment without resorting to syntaxic complexities ?
It is not possible to achieve the equivalent of your hypothetical:
using AlignedType = alignas(32) T;
There is no such thing as a type that is equivalent to another type but with different alignment. If the language allowed something like that to exist, imagine all the extra overload resolution rules we would have to add to the language.
The syntax
new (std::align_val_t(1024)) T;
should work. However, MSVC has a bug where it considers ::operator new(std::size_t, std::align_val_t)
to be a "placement allocation function" rather than a "usual allocation function". This leads to the error you are seeing, where it complains that a placement allocation function matches a usual deallocation function.
(It seems that it is the standard's fault for not being clear: CWG2592. However, MSVC is to blame too; why did they choose to interpret the standard in a way that makes new (std::align_val_t(x)) T
illegal?)
A workaround is to implement your own operator new
and matching operator delete
that delegate to the ones that you actually want to call:
struct alignment_tag {};
void* operator new(std::size_t size, alignment_tag, std::align_val_t alignment) {
return operator new(size, alignment);
}
void operator delete(void* p, alignment_tag, std::align_val_t alignment) {
operator delete(p, alignment);
}
Now you can do
T *variable = new (alignment_tag{}, std::align_val_t(1024)) T;
I personally would wrap this in something like:
template <class T>
struct AlignedDeleter {
AlignedDeleter(std::align_val_t alignment) : alignment_(alignment) {}
void operator()(T* ptr) const {
ptr->~T();
::operator delete(ptr, alignment_);
}
std::align_val_t alignment_;
};
template <typename T>
using AlignedUniquePtr = std::unique_ptr<T, AlignedDeleter<T>>;
template <typename T, typename... Args>
AlignedUniquePtr<T> make_unique_aligned(std::align_val_t alignment, Args&&... args) {
return AlignedUniquePtr<T>(new (alignment_tag{}, alignment) T(std::forward<Args>(args)...), AlignedDeleter<T>(alignment));
}
(You can always call .release()
if you need to.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With