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
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.