Barry Barry - 3 days ago 5
C++ Question

reinterpret_cast creating a trivially default-constructible object

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 with
std::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:


  1. Is that quote correct?

  2. If yes, at what point does the lifetime of the
    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
    ?

  3. Does anything change between C++11 and C++1z in this regard?






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.

Answer

There is no living X object, 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.

Comments