Reputation: 4249
I want to construct using placement-new an object of arbitrary type inside a std::aligned_union_t
. Once constructed successfully, I want to be able to get back the pointer to the constructed object without storing it separately. Is it legal to do so by simply reinterpret_cast
'ing the std::aligned_union_t
, as long as I ensure that I cast it to the original type that was constructed?
Is the following code that exemplifies the above, legal? Are there any type trait requirements that MyStruct
should satisfy for it to be so? For instance, does it have to be a POD?
#include <type_traits>
#include <memory>
#include <cstddef>
#include <exception>
struct MyStruct
{
int value = 0;
};
constexpr size_t c_alignedUnionSize = 10;
std::aligned_union_t<c_alignedUnionSize, std::max_align_t> g_storage;
MyStruct* GetPtr()
{
return reinterpret_cast<MyStruct*>(std::addressof(g_storage));
}
void Construct()
{
if (sizeof(MyStruct) > sizeof(g_storage))
{
std::terminate();
}
auto ptr = new (std::addressof(g_storage)) MyStruct{};
if (!ptr)
{
std::terminate();
}
GetPtr()->value = 123;
}
void Destroy()
{
GetPtr()->~MyStruct();
}
int GetValue()
{
return GetPtr()->value;
}
int main()
{
Construct();
auto value = GetValue();
Destroy();
return value;
}
Upvotes: 4
Views: 243
Reputation: 18081
Unfortunately this is forbidden by the standard. In the C++ standard reinterpret_cast
from one pointer to an object a to another object b of different type is stated to be valid only when the two object are pointer interconvertible, [basic.compound]/4:
Two objects a and b are pointer-interconvertible if:
they are the same object, or
one is a union object and the other is a non-static data member of that object ([class.union]), or
one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object ([class.mem]), or
there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a
reinterpret_cast
. [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note ]
The fact that a pointer has the right type and right value (memory address) does not make it a valid pointer. The canonical example of such surprising behavior is this:
alignas(int) unsigned char buff[2*sizeof(int)];
auto p1 = new(buff) int{};
auto p2 = new(buff+sizeof(int)) int{};
*(p1+1) = 10;//Undefined behavior
//p1+1 does not point to *p2 even if p1 and p2 have same type and value.
So, to be standard compliant, you must store the value of the pointer returned by new
.
I found a good solution that consists to cast the pointer to an integer type and that to an other pointer type, that will cause an implementation defined behavior ([expr.reinterpret_cast]/5):
reinterpret_cast<MyStruct*>(reinterpret_cast<std::uintptr_t>(addressof(g_storage));
Upvotes: 1
Reputation: 93364
reinterpret_cast
here should be safe. The latest Standard draft says:
An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue
v
of object pointer type is converted to the object pointer type “pointer to cvT
”, the result isstatic_cast<cv T*>(static_cast<cv void*>(v))
. [ Note: Converting a prvalue of type “pointer toT1
” to the type “pointer toT2
” (whereT1
andT2
are object types and where the alignment requirements ofT2
are no stricter than those ofT1
) and back to its original type yields the original pointer value. — end note ]
Related questions:
Upvotes: 1