reinterpret_cast / static_cast and undefined behavior
Asked Answered
N

1

7

In a variant class I'm working on the raw storage is a char array:

alignas(/* the strictest alignment of all types of the variant */)
char storage[/* ... */];

The assignment operator is something like:

template<class X>
void operator=(const X &x)
{
  // ...code for clearing the storage and setting the tag for type X...

  new(storage) X(x);
}

while the code for getting the stored object is:

template<class X>
const X &get()
{
  // ...
  return *reinterpret_cast<X *>(storage);
  // ...
}

It seems to work but is it always well defined? I'm worried about safely dereferencing the pointer (is it allowed by the type aliasing rules?).

Are there any differences between the current implementation and

 return *static_cast<const X *>(static_cast<const void *>(storage));

Related question/answer:

https://mcmap.net/q/16600/-do-i-understand-c-c-strict-aliasing-correctly (see James Kanze's comments).


EDIT

Second question already has an answer here: C++ When should we prefer to use a two chained static_cast over reinterpret_cast

Naivete answered 7/2, 2015 at 11:1 Comment(2)
Surely this new(storage) X(x); is a memory leakFishtail
@EdHeal It constructs X in the storage by means of placement new. It should be a safe practice for aligned storage (e.g. #4583625). Could you add some detail?Naivete
P
4

As storage is correctly aligned, I cannot imagine where a problem could arise. The paragraph(*) 4.10 on pointer conversions says : A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a non-null pointer value of a pointer to object type to a “pointer to cv void” represents the address of the same byte in memory as the original pointer value.

Concerning your second question, paragraph 5.2.10 on reinterpres_cast : 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<cv T*>(static_cast<cv void*>(v)) where cv stands for a optional const or volatile.

So this part is guaranteed per specs. More as we saw that a cast to void * should point to first byte of memory, there is no UB for my understanding of the standard ... provided compilers have same understanding ;-)

(*) Référence : Draft for current C++ specification

Patriapatriarch answered 7/2, 2015 at 14:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.