cppreference† states that:
Objects with trivial default constructors can be created by using
reinterpret_cast
on any suitably aligned storage, e.g. on memory allocated withstd::malloc
.
This implies that the following is well-defined code:
struct X { int x; }; alignas(X) char buffer[sizeof(X)]; // (A) reinterpret_cast<X*>(buffer)->x = 42; // (B)
Three questions follow:
X
begin? If on line (B)
, is it the cast itself that is considered acquiring storage? If on line (A)
, what if there were a branch between (A)
and (B)
that would conditionally construct an X
or some other pod, Y
?†Note that this is an old link. The wording was changed in response to this question. It now reads:
Unlike in C, however, objects with trivial default constructors cannot be created by simply reinterpreting suitably aligned storage, such as memory allocated with
std::malloc
: placement-new is required to formally introduce a new object and avoid potential undefined behavior.
Anyway, the consequence of this is, that reinterpret_cast<> is portable as long as you do not rely on the byte order in any way. Your example code does not rely on byte order, it treats all bytes the same (setting them to zero), so that code is portable.
No. It is a purely compile-time construct. It is very dangerous, because it lets you get away with very wrong conversions.
reinterpret_cast is a type of casting operator used in C++. It is used to convert a pointer of some data type into a pointer of another data type, even if the data types before and after conversion are different. It does not check if the pointer type and data pointed by the pointer is same or not.
There is no X
object, living or otherwise, so pretending that there is one results in undefined behavior.
[intro.object]/1 spells out exhaustively when objects are created:
An object is created by a definition ([basic.def]), by a new-expression ([expr.new]), when implicitly changing the active member of a union ([class.union]), or when a temporary object is created ([conv.rval], [class.temporary]).
With the adoption of P0137R1, this paragraph is the definition of the term "object".
Is there a definition of an X
object? No. Is there a new-expression? No. Is there a union? No. Is there a language construct in your code that creates a temporary X
object? No.
Whatever [basic.life] says about the lifetime of an object with vacuous initialization is irrelevant. For that to apply, you have to have an object in the first place. You don't.
C++11 has roughly the same paragraph, but doesn't use it as the definition of "object". Nonetheless, the interpretation is the same. The alternative interpretation - treating [basic.life] as creating an object as soon as suitable storage is obtained - means that you are creating Schrödinger's objects*, which contradicts N3337 [intro.object]/6:
Two objects that are not bit-fields may have the same address if one is a subobject of the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they shall have distinct addresses.
* Storage with the proper alignment and size for a type T
is by definition storage with the proper alignment and size for every other type whose size and alignment requirements are equal to or less than those of T
. Thus, that interpretation means that obtaining the storage simultaneously creates an infinite set of objects with different types in said storage, all having the same address.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With