I'm using std::aligned_storage
as the backing storage for a variant template. The problem is, once I enable -O2
on gcc I start getting warnings of 'dereferencing type-punned pointer will break strict aliasing`.
The real template is much more complex (type checked at runtime), but a minimal example to generate the warning is:
struct foo
{
std::aligned_storage<1024> data;
// ... set() uses placement new, stores type information etc ...
template <class T>
T& get()
{
return reinterpret_cast<T&>(data); // warning: breaks strict aliasing rules
}
};
I'm pretty sure boost::variant
is doing essentially the same thing as this, but I can't seem to find how they avoid this issue.
My questions are:
aligned_storage
in this way violates strict-aliasing, how should I be using it?get()
given that there are no other pointer based operations in the function?
get()
is inlined?get() = 4; get() = 3.2
? Could that sequence be reordered due to int
and float
being different types?The strict aliasing rule dictates that pointers are assumed not to alias if they point to fundamentally different types, except for char* and void* which can alias to any other data type.
In both C and C++ the standard specifies which expression types are allowed to alias which types. The compiler and optimizer are allowed to assume we follow the aliasing rules strictly, hence the term strict aliasing rule.
std::aligned_storage
is part of <type_traits>
; like most of the rest of the inhabitants of that header file, it is just a holder for some typedefs and is not meant to be used as a datatype. Its job is to take a size and alignment, and make you a POD type with those characteristics.
You cannot use std::aligned_storage<Len, Align>
directly. You must use std::aligned_storage<Len, Align>::type
, the transformed type, which is "a POD type suitable for for use as uninitialized storage for any object whose size is at most Len
and whose alignment is a divisor of Align
." (Align
defaults to the largest useful alignment greater than or equal to Len
.)
As the C++ standard notes, normally the type returned by std::aligned_storage
will be an array (of the specified size) of unsigned char
with an alignment specifier. That avoids the "no strict aliasing" rule because a character type may alias any other type.
So you might do something like:
template<typename T>
using raw_memory = typename std::aligned_storage<sizeof(T),
std::alignment_of<T>::value>::type;
template<typename T>
void* allocate() { return static_cast<void*>(new raw_memory<T>); }
template<typename T, typename ...Arg>
T* maker(Arg&&...arg) {
return new(allocate<T>()) T(std::forward<Arg>(arg)...);
}
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