Note: This question has been renamed and reduced to make it more focused and readable. Most of the comments refer to the old text.
According to the standard, objects of different type may not share the same memory location. So this would not be legal:
std::array<short, 4> shorts;
int* i = reinterpret_cast<int*>(shorts.data()); // Not OK
The standard, however, allows an exception to this rule: any object may be accessed through a pointer to char
or unsigned char
:
int i = 0;
char * c = reinterpret_cast<char*>(&i); // OK
However, it is not clear to me whether this is also allowed the other way around. For example:
char * c = read_socket(...);
unsigned * u = reinterpret_cast<unsigned*>(c); // huh?
Regarding the validity of …
The
reinterpret_cast
itself is OK or not, in the sense of producing a useful pointer value, depending on the compiler. And in this example the result isn't used, in particular, the character array isn't accessed. So there is not much more that can be said about the example as-is: it just depends.But let's consider an extended version that does touch on the aliasing rules:
And let's only consider the case where the compiler guarantees a useful pointer value, one that would place the pointee in the same bytes of memory (the reason that this depends on the compiler is that the standard, in §5.2.10/7, only guarantees it for pointer conversions where the types are alignment-compatible, and otherwise leave it as "unspecified" (but then, the whole of §5.2.10 is somewhat inconsistent with §9.2/18).
Now, one interpretation of the standard's §3.10/10, the so called "strict aliasing" clause (but note that the standard does not ever use the term "strict aliasing"),
is that, as it itself says, concerns the dynamic type of the object residing in the
c
bytes.With that interpretation, the read operation on
*p
is OK iffoo
has placed anint
object there, and otherwise not. So in this case, achar
array is accessed via anint*
pointer. And nobody is in any doubt that the other way is valid: even thoughfoo
may have placed anint
object in those bytes, you can freely access that object as a sequence ofchar
values, by the last dash of §3.10/10.So with this (usual) interpretation, after
foo
has placed anint
there, we can access it aschar
objects, so at least onechar
object exists within the memory region namedc
; and we can access it asint
, so at least that oneint
exists there also; and so David’s assertion in another answer thatchar
objects cannot be accessed asint
, is incompatible with this usual interpretation.David's assertion is also incompatible with the most common use of placement new.
Regarding what other possible interpretations there are, that perhaps could be compatible with David's assertion, well, I can't think of any that make sense.
So in conclusion, as far as the Holy Standard is concerned, merely casting oneself a
T*
pointer to the array is practically useful or not depending on the compiler, and accessing the pointed to could-be-value is valid or not depending on what's present. In particular, think of a trap representation ofint
: you would not want that blowing up on you, if the bitpattern happened to be that. So to be safe you have to know what's in there, the bits, and as the call tofoo
above illustrates the compiler can in general not know that, like, the g++ compiler's strict alignment-based optimizer can in general not know that…Some of your code is questionable due to the pointer conversions involved. Keep in mind that in those instances
reinterpret_cast<T*>(e)
has the semantics ofstatic_cast<T*>(static_cast<void*>(e))
because the types that are involved are standard-layout. (I would in fact recommend that you always usestatic_cast
viacv void*
when dealing with storage.)A close reading of the Standard suggests that during a pointer conversion to or from
T*
it is assumed that there really is an actual objectT*
involved -- which is hard to fulfill in some of your snippet, even when 'cheating' thanks to the triviality of types involved (more on this later). That would be besides the point however because...Aliasing is not about pointer conversions. This is the C++11 text that outlines the rules that are commonly referred to as 'strict aliasing' rules, from 3.10 Lvalues and rvalues [basic.lval]:
(This is paragraph 15 of the same clause and subclause in C++03, with some minor changes in the text with e.g. 'lvalue' being used instead of 'glvalue' since the latter is a C++11 notion.)
In the light of those rules, let's assume that an implementation provides us with
magic_cast<T*>(p)
which 'somehow' converts a pointer to another pointer type. Normally this would bereinterpret_cast
, which yields unspecified results in some cases, but as I've explained before this is not so for pointers to standard-layout types. Then it's plainly true that all of your snippets are correct (substitutingreinterpret_cast
withmagic_cast
), because no glvalues are involved whatsoever with the results ofmagic_cast
.Here is a snippet that appears to incorrectly use
magic_cast
, but which I will argue is correct:To justify my reasoning, assume this superficially different snippet:
This snippet is carefully constructed. In particular, in
new (&c) int;
I'm allowed to use&c
even thoughc
was destroyed due to the rules laid out in paragraph 5 of 3.8 Object lifetime [basic.life]. Paragraph 6 of same gives very similar rules to references to storage, and paragraph 7 explains what happens to variables, pointers and references that used to refer to an object once its storage is reused -- I will refer collectively to those as 3.8/5-7.In this instance
&c
is (implicitly) converted tovoid*
, which is one of the correct use of a pointer to storage that has not been yet reused. Similarlyp
is obtained from&c
before the newint
is constructed. Its definition could perhaps be moved to after the destruction ofc
, depending on how deep the implementation magic is, but certainly not after theint
construction: paragraph 7 would apply and this is not one of the allowed situations. The construction of theshort
object also relies onp
becoming a pointer to storage.Now, because
int
andshort
are trivial types, I don't have to use the explicit calls to destructors. I don't need the explicit calls to the constructors, either (that is to say, the calls to the usual, Standard placement new declared in<new>
). From 3.8 Object lifetime [basic.life]:This means that I can rewrite the code such that, after folding the intermediate variable
q
, I end up with the original snippet.Do note that
p
cannot be folded away. That is to say, the following is defintively incorrect:If we assume that an
int
object is (trivially) constructed with the second line, then that must mean&c
becomes a pointer to storage that has been reused. Thus the third line is incorrect -- although due to 3.8/5-7 and not due to aliasing rules strictly speaking.If we don't assume that, then the second line is a violation of aliasing rules: we're reading what is actually a
char c[sizeof(int)]
object through a glvalue of typeint
, which is not one of the allowed exception. By comparison,*magic_cast<unsigned char>(&c) = 42;
would be fine (we would assume ashort
object is trivially constructed on the third line).Just like Alf, I would also recommend that you explicitly make use of the Standard placement new when using storage. Skipping destruction for trivial types is fine, but when encountering
*some_magic_pointer = foo;
you're very much likely facing either a violation of 3.8/5-7 (no matter how magically that pointer was obtained) or of the aliasing rules. This means storing the result of the new expression, too, since you most likely can't reuse the magic pointer once your object is constructed -- due to 3.8/5-7 again.Reading the bytes of an object (this means using
char
orunsigned char
) is fine however, and you don't even to usereinterpret_cast
or anything magic at all.static_cast
viacv void*
is arguably fine for the job (although I do feel like the Standard could use some better wording there).That is not correct. The aliasing rules state under which circumstances it is legal/illegal to access an object through an lvalue of a different type. There is an specific rule that says that you can access any object through a pointer of type
char
orunsigned char
, so the first case is correct. That is, A => B does not necessarily mean B => A. You can access anint
through a pointer tochar
, but you cannot access achar
through a pointer toint
.For the benefit of Alf: