Some of your codes are questionable due to pointer conversions. Keep in mind that in these cases reinterpret_cast<T*>(e) has the semantics of static_cast<T*>(static_cast<void*>(e)) , because the types used are standard. (I would recommend that you always use static_cast through cv void* when working with storage.)
Reading the Standard privately, it is assumed that while converting the pointer to or from T* it is assumed that there really is an actual T* object that is difficult to execute in some fragments, even if you “cheat” it because of the triviality of the types involved (more on this later). It would be, moreover, because ...
Aliasing is not a conversion of pointers. . This is a C ++ 11 text that describes the rules, which are usually called the "strict anti-aliasing" rules, from 3.10 Lvalues and rvalues [basic.lval]:
10 If a program tries to access the stored value of an object through a gl value other than one of the following types, the behavior is undefined:
- dynamic type of object
- cv-qualified version of the dynamic type of an object,
- a type similar (as defined in 4.4) for the dynamic type of an object,
- a type that is a signed or unsigned type corresponding to a dynamic type of an object,
- a type that is a signed or unsigned type corresponding to the receipt version of the dynamic type of an object,
- an aggregate or union type that includes one of the above types among its elements or non-static data members (including a recursively element or non-static data element of a sub-aggregate or contained union),
- a type that is (possibly cv-qualified) a base class type of a dynamic object type,
- a char or unsigned char type.
(This is paragraph 15 of the same sentence and subclause in C ++ 03 with some minor changes to the text, using, for example, 'lvalue' instead of 'glvalue', since the latter is a C ++ 11 concept.)
In light of these rules, suppose the implementation provides us with magic_cast<T*>(p) , which somehow converts a pointer to another type of pointer. Usually it will be reinterpret_cast , which in some cases gives unspecified results, but, as I already explained, this is not so for pointers to types of the standard layout. Then it’s true that all of your fragments are correct (substituting reinterpret_cast with magic_cast ), because no glvalues are associated with the results of magic_cast .
Here is a snippet that seems to misuse magic_cast , but I will argue correctly:
To justify my reasoning, suppose this fragment is superficially different:
// alignment same as before alignas(alignment) char c[sizeof(int)]; auto p = magic_cast<int*>(&c); // end lifetime of c c.~decltype(c)(); // reuse storage to construct new int object new (&c) int; *p = 42; auto q = magic_cast<short*>(p); // end lifetime of int object p->~decltype(0)(); // reuse storage again new (p) short; *q = 42;
This fragment is carefully designed. In particular, in new (&c) int; I am allowed to use &c , although c was destroyed due to the rules outlined in paragraph 5 of 3.8 Object lifetime [basic.life]. Paragraph 6 of them contains very similar rules for references to the repository, and paragraph 7 explains what happens to the variables, pointers and links that were used to refer to the object after reusing its storage - I will refer to everything together as 3.8 / 5-7.
In this case, &c (implicitly) is converted to void* , which is one of the correct uses of a pointer to a repository that has not yet been reused. Similarly, p obtained from &c before creating a new int . Its definition, perhaps, can be transferred after the destruction of c , depending on the depth of the magic of implementation, but, of course, not after the construction of int : paragraph 7 will be applied, and this is not one of the permitted situations. The construction of the short object also relies on p , becoming a pointer to the repository.
Now, since int and short are trivial types, I should not use explicit calls to destructors. I also do not need explicit calls to constructors (that is, calls to the normal, standard placement new declared in <new> ). From 3.8 Object lifetime [basic.life]:
1 [...] The lifetime of an object of type T begins when:
- stores with proper alignment and size for type T and
- If the object has non-trivial initialization, its initialization is complete.
The lifetime of an object of type T ends when:
- if T is a class type with a nontrivial destructor (12.4), a call to the destructor begins, or
- the storage that the object is occupying, reusing, or freeing up.
This means that I can rewrite the code in such a way that after folding the intermediate variable q I get the source fragment.
Note that p cannot be complex. In other words, the following is not true:
alignas(alignment) char c[sizeof(int)]; *magic_cast<int*>(&c) = 42; *magic_cast<short*>(&c) = 42;
Assuming the int object is (trivially) built with the second line, this means that &c becomes a pointer to the repository that has been reused. Thus, the third line is incorrect - although strictly due to 3.8 / 5-7 and not due to smoothing rules.
If we do not assume that the second line is a violation of anti-aliasing rules: we read what is actually a char c[sizeof(int)] object through a glvalue of type int , which is not one of the allowed exceptions. For comparison, *magic_cast<unsigned char>(&c) = 42; would be accurate (we would assume that the short object is trivially built on the third line).
As with Alf, I also recommend that you use standard allocation when using storage. Skipping the kill for trivial types is fine, but if you come across *some_magic_pointer = foo; , you are likely to encounter either a violation of 3.8 / 5-7 (regardless of how magically this pointer was received) or anti-aliasing rules. It also means saving the result of the new expression, since you most likely will not be able to reuse the magic pointer after creating the object - because of 3.8 / 5-7 again.
Reading the bytes of the object (this means using char or unsigned char ) is fine, but you can't even use reinterpret_cast or anything magic at all. static_cast via cv void* is probably great for work (although it seems to me that the standard can use the best wording there).