Can std::launder be used to convert an object pointer to its enclosing array pointer?

This depends on whether the enclosing array object is a complete object, and if not, whether you can validly access more bytes through a pointer to that enclosing array object (e.g., because it’s an array element itself, or pointer-interconvertible with a larger object, or pointer-interconvertible with an object that’s an array element). The “reachable” requirement means that you cannot use launder to obtain a pointer that would allow you to access more bytes than the source pointer value allows, on pain of undefined behavior. This ensures that the possibility that some unknown code may call launder does not affect the compiler’s escape analysis.

I suppose some examples could help. Each example below reinterpret_casts a int* pointing to the first element of an array of 10 ints into a int(*)[10]. Since they are not pointer-interconvertible, the reinterpret_cast does not change the pointer value, and you get a int(*)[10] with the value of “pointer to the first element of (whatever the array is)”. Each example then attempts to obtain a pointer to the entire array by calling std::launder on the cast pointer.

int x[10];
auto p = std::launder(reinterpret_cast<int(*)[10]>(&x[0])); 

This is OK; you can access all elements of x through the source pointer, and the result of the launder doesn’t allow you to access anything else.

int x2[2][10];
auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0])); 

This is undefined. You can only access elements of x2[0] through the source pointer, but the result (which would be a pointer to x2[0]) would have allowed you to access x2[1], which you can’t through the source.

struct X { int a[10]; } x3, x4[2]; // assume no padding
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK

This is OK. Again, you can’t access through a pointer to x3.a any byte you can’t access already.

auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0])); 

This is (intended to be) undefined. You would have been able to reach x4[1] from the result because x4[0].a is pointer-interconvertible with x4[0], so a pointer to the former can be reinterpret_cast to yield a pointer to the latter, which then can be used for pointer arithmetic. See https://wg21.link/LWG2859.

struct Y { int a[10]; double y; } x5;
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0])); 

And this is again undefined, because you would have been able to reach x5.y from the resulting pointer (by reinterpret_cast to a Y*) but the source pointer can’t be used to access it.

Leave a Comment