简体   繁体   中英

Is it never truly safe to reinterpret_cast input into std::unique_ptr?

When using various API's that have variable size structures (structures that must be allocated as byte[] and then cast into the struct), it would be nice if the unique_ptr holder could point to the structure, since that's what we'll be using.

Example:

std::unique_ptr<VARIABLE_SIZE_STRUCT[]> v; 
v.reset(reinterpret_cast<VARIABLE_SIZE_STRUCT*>(new BYTE[bytesRequired]));

This allowed `v to provide the view to the structure itself, which is preferable because that we don't need a second variable and we don't care about the byte pointer except for deletion.

The problem comes in with the possibility of thunking the pointer on the cast (making it unsafe to free). I see no reasonable reason why the compiler would change the pointer value on cast (since there's no inheritance), but I hear that the standard reserves the right to thunk any pointer on any cast, so as far as standard-compliant coding goes, this approach is out the window, right? Or is there some reason it is safe? Is there a way to at least static_assert this, or some other way to make it safe or cleanly deal with this type of structure?

You're correct, this is unsafe. It is possible to make it safe, however.

The standard guarantees that if you reinterpret_cast to a different type, then back to the original type, you get back the original value.

You can use this along with a custom deleter, to ensure your internal pointer is cast back to the type it was allocated as before releasing it.

auto deleter = [](VARIABLE_SIZE_STRUCT* ptr) 
{ 
    delete[] reinterpret_cast<uint8_t*>(ptr); 
}; 

std::unique_ptr<VARIABLE_SIZE_STRUCT, decltype(deleter)> v
    (reinterpret_cast<VARIABLE_SIZE_STRUCT*>(new uint8_t[256]), deleter);

At this point, you're probably better off creating your own wrapper and not using unique_ptr .

  • your allocation may not have the alignment needed by VARIABLE_SIZE_STRUCT

  • the allocated memory has not had an object of VARIABLE_SIZE_STRUCT placement- new ed in it - assuming you take care of that, the unique_ptr 's default destructor logic should find the expected object instance to destruct, but the deallocation itself would then not be done with delete [] on a BYTE* - to have defined behaviour you'd have to customise the deleter to invoke first ~VARIABLE_SIZE_STRUCT() then delete[] ...

If you're concerned about "thunking", you could do a check at run-time:

BYTE* p;
v.reset(reinterpret_cast<VARIABLE_SIZE_STRUCT*>(p = new BYTE[bytesRequired]));
assert(reinterpret_cast<BYTE*>(v.get()) == p);

The background on that - 5.2.10/7:

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 cv T”, the result is static_cast<cvT*>(static_cast<cv void*>(v)) . Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value.

So, if the alignment requirements for VARIABLE_SIZE_STRUCT are stricter than for BYTE , you're not guaranteed to retrieve the original pointer using reinterpret_cast<BYTE*> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM