I was reading the docs for std::launder and I came across this code snippet:
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
What is int(*)[10]
supposed to mean?
I read it as "array of ints of size 10". But then the UB comment doesn't make sense to me. x2[0]
and p2
are both size 10. As long as you don't try to do arithmetic above the 10th element (eg p2+10
) then you shouldn't be in UB land, right?
The closest thing I found to an answer is provided in 71631428, but it doesn't fully answer my questions.
Edit:
What is
int(*)[10]
supposed to mean?
I finally found the answer and it is so basic knowledge: "Pointer to an array of ints of size 10". The other question about UB remains. So I will edit the title appropriately.
Short answer: C++ standards require that only elements reachable through the original object’s type should be accessed.
Long Answer:
Have a look at what the memory layout of x2
may look like
x2[0][0], x2[0][1], ..., x2[0][9], x2[1][0], x2[1][1], ..., x2[1][9]
The addresses are contagious and the elements are laid in a single block of memory that you can iterate over.
This piece of code reinterpret_cast<int(*)[10]>(&x2[0][0]);
tells the compiler to treat the memory at x2[0][0]
as the start of an array of 10 ints however this address is nothing but a single integer inside a larger container.
But because it's C++, and you are using reinterpret_cast
, you can indeed do something like this
(*p2)[11] = 2; /* this code should access x2[1][1] */
So with all that said (elements are contiguous in memory and in C++ you can do almost anything), a line like this (*p2)[11] = 2;
will work but since you can't access x2[11]
from the address given by the source &x2[][]
, this code is identified by the standards as an undefined behavior.