Search code examples
c++initializationlanguage-lawyernew-operator

Assigning to uninitialized memory in C++


If I have code like this that would be legal in C:

int* i = static_cast<int*>(std::malloc(sizeof(int)));
*i = 123;

Is this also legal C++, or would it invoke undefined/implementation-defined behavior? And for what types would it be legal?

Or would it be needed to do new(i) int (123); already in this case?

For non-trivial classes for example, it could not be legal, because the object is never constructed, but then its operator= is called.


Solution

  • This is legal since C++20. It was technically illegal before that, but worked in practice anyway.

    This normally would be UB due to accessing an object before its lifetime starts, but C++20 added [intro.object]/11:

    Some operations are described as implicitly creating objects within a specified region of storage. For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types ([basic.types.general]) in its specified region of storage if doing so would result in the program having defined behavior. If no such set of objects would give the program defined behavior, the behavior of the program is undefined. If multiple such sets of objects would give the program defined behavior, it is unspecified which such set of objects is created.

    [Note 4: Such operations do not start the lifetimes of subobjects of such objects that are not themselves of implicit-lifetime types. — end note]

    malloc() is blessed for this in [c.malloc]/4:

    These functions implicitly create objects ([intro.object]) in the returned region of storage and return a pointer to a suitable created object. In the case of calloc and realloc, the objects are created before the storage is zeroed or copied, respectively.

    (Note "returns a pointer to a suitable created object", otherwise std::launder() would be required.)

    This works for "implicit lifetime types": [basic.types.general]/9

    ... Scalar types, implicit-lifetime class types ([class.prop]), array types, and cv-qualified versions of these types are collectively called implicit-lifetime types.

    [class.prop]/9

    A class S is an implicit-lifetime class if

    (9.1) — it is an aggregate whose destructor is not user-provided or

    (9.2) — it has at least one trivial eligible constructor and a trivial, non-deleted destructor.

    (9.1) sounds confusing until you read "Note 4" in the first quote in this answer.