Reputation: 1622
I was surprised by the results I'm seeing in VC++ 2015, and need help understanding how it works.
struct MyType
{
MyType(int x_) : x(x_) { }
int x;
};
auto u = std::make_unique<MyType>(10);
void* pv = &u;
This obviously fails because u
's address is not a pointer to MyType
:
MyType *pM = (MyType*)pv;
But this works, pM2
gets the address of the MyType
object stored in u
:
MyType** ppM = (MyType**)pv;
MyType* pM2 = *ppM;
Is there anything in the standard that says this is supposed to work? Or is it only working due to a non-portable implementation detail of my compiler? Something that allows me to treat unique_ptr
like a pointer-to-pointer in a round about way?
And before you say, "that's stupid, don't use void*
or C-style casts", please understand that I'm working with legacy code that handles serialization of structs through void pointers and offsets to struct members. I can't change that part right now. But I want to use a unique_ptr
for a struct member to simplify memory ownership and cleanup. And I'd like to know how fragile my unique_ptr
is in this legacy environment.
Upvotes: 0
Views: 1389
Reputation: 20396
This is basically just you getting lucky.
In the ABI of your particular compiler, the T*
that stores the object maintained by the unique_ptr
is the first member of the object, so it has the same address as the object itself. In much the same way as this example:
struct container {
int val;
};
int main() {
container c{15};
intptr_t val1 = reinterpret_cast<intptr_t>(&c);
intptr_t val2 = reinterpret_cast<intptr_t>(&(c.val));
assert(val1 == val2); //will pretty much always be true
}
Of course, this is not behavior you should depend on! It's unspecified by the Standard, and could change if the vendor decides they have a better format for storing pointers inside std::unique_ptr
.
Upvotes: 2
Reputation: 2073
Essentially you are doing something like this:
std::unique_ptr<MyType> up = ...;
MyType* p = *reinterpret_cast<MyType**>(&up);
With some detours and C-style casts. You take the pointer to the unique_ptr
and reinterpret it as a pointer to pointer of MyType
This is pure luck and results in undefined behavior, you shouldn't use this type of code for any reason. If you need the internal pointer use the get()
method on unique_ptr.
Upvotes: 1
Reputation: 275895
This is undefined behavior that happens to work because the unique pointer happens to store only a single pointer as its state, and that state is a pointer to T
.
Undefined behavior can do anything, including time travel and format your hard drive. I know people say this and others think it is a joke, but these are actually true statements you can experimentally verify.
As it happens, your undefined behavior here has reinterpreted some memory in a way that "works".
You cannot serialize/deserialize non-pod structures in a defined way using your library. You can hack it to work, but any compiler update (even a compiler flag update!) could suddenly behave completely differently.
Consider having a structure for serialization/deserialization, and another for runtime use. Marshall from one to the other. Yes, this sucks.
Upvotes: 0