Just a couple weeks ago, I learned that the C++ Standard had a strict aliasing rule. Basically, I had asked a question about shifting bits -- rather than shifting each byte
The aliasing rule means that the language only promises your pointer dereferences to be valid (i.e. not trigger undefined behaviour) if:
D* d
pointing to a valid D, accessing the pointer returned by static_cast<B*>(d)
is OK, but accessing that returned by reinterpret_cast<B*>(d)
is not. The latter may have failed to account for the layout of the B sub-object inside D.char
. Since char is byte-sized and byte-aligned, there is no way you could not be able to read data from a char*
while being able to read it from a D*
.That said, other rules in the standard (in particular those about array layout and POD types) can be read as ensuring that you can use pointers and reinterpret_cast<T*>
to alias two-way between POD types and char
arrays if you make sure to have a char array of the apropriate size and alignment.
In other words, this is legal:
int* ia = new int[3];
char* pc = reinterpret_cast<char*>(ia);
// Possibly in some other function
int* pi = reinterpret_cast<int*>(pc);
While this may invoke undefined behaviour:
char* some_buffer; size_t offset; // Possibly passed in as an argument
int* pi = reinterpret_cast<int*>(some_buffer + offset);
pi[2] = -5;
Even if we can ensure that the buffer is big enough to contain three int
s, the alignment might not be right. As with all instances of undefined behaviour, the compiler may do absolutely anything. Three common ocurrences could be:
Since you always want to ward off UB like the devil itself, you need a char
array with the correct size and alignment. The easiest way to get that is simply to start with an array of the "right" type (int in this case), then fill it through a char pointer, which would be allowed since int is a POD type.
Addendum: after using placement new
, you will be able to call any function on the object. If the construction is correct and does not invoke UB due to the above, then you have successfully created an object at the desired place, so any calls are OK, even if the object was non-POD (e.g. because it had virtual functions). After all, any allocator class will likely use placement new to create the objects in the storage that they obtain. Note that this only necessarily true if you use placement new
; other usages of type punning (e.g. naïve serialization with fread/fwrite) may result in an object that is incomplete or incorrect because some values in the object need to be treated specially to maintain class invariants.
As a matter of fact, explanation of standard rule regarding pointer type punning through strict aliasing is not neccessarily correct or easy to understand. Standard doesn't mention 'strict aliasing', and I find original standard wording easier to understand and reason about.
In essence, it says that you can only access an object thorugh a pointer to the related type which is suited to access this object (such as the same type or related class type) or through a pointer to char
.
As you see, the question of 'two-way street' is not even applicable.