c++c++17stdlaunder

reachability with std::launder


I'm trying to understand the following snippet from CPP reference. Can someone explain in a little more detail why x2[1] is unreachable from source ? Can't we reach it via &x2[0][0] + 10 for example?

int x2[2][10];
auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0])); 
// Undefined behavior: x2[1] would be reachable through the resulting pointer to x2[0]
// but is not reachable from the source

Solution

  • The reachability condition basically asks whether it is possible to access a given byte of memory via pointer arithmetic and reinterpret_cast from a given pointer. The technical definition, which is effectively the same, is given on the linked cppreference page:

    (bytes are reachable through a pointer that points to an object Y if those bytes are within the storage of an object Z that is pointer-interconvertible with Y, or within the immediately enclosing array of which Z is an element)

    x2 is an array of 2 arrays of 10 arrays of int. Let's suppose we call the two arrays a and b.

    &x2[0][0] is an int* pointing to the first element of a.

    &x2[0][0] + 10 is an int* pointer one-past the last element a. The address of this pointer value is also the address at which b begins. However one cannot obtain a pointer to b or one of its elements via reinterpret_cast since &x2[0][0] + 10 doesn't point to any object that is pointer-interconvertible with b or one of its elements.

    In terms of the technical definition, the only object pointer-interconvertible with the first element of a is the object itself. Therefore the reachable bytes from a pointer to the first element of a are only the bytes of a, which is the array immediately enclosing this object.

    Therefore the reachable bytes through &x2[0][0] are only those of the array a, not including b. E.g. *(&x2[0][0] + 10) = 123; has undefined behavior.

    However if std::launder were to return a pointer to a (of type int(*)[10]), then (p2+1)[i] would be a way to access all elements of b. Or in terms of the technical definition, the array immediately enclosing the object p2 points to would be x2, so that all bytes of x2 are reachable.

    This means after the std::launder bytes that weren't reachable before would become reachable. Therefore the call has undefined behavior.