std::launder example has this block of code:
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? Example from std::aligned_storage
makes it seem to me like that's not the case:
std::aligned_storage_t<sizeof(T), alignof(T)> data[N];
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
// Note: std::launder is needed after the change of object model in P0137R1
return *std::launder(reinterpret_cast<const T*>(&data[pos]));
}
Here's where confusion comes in: &data[pos]
is just &data[pos][0]
, because &p
== &p[0]
where p
is an array.
How come this is okay if std::aligned_storage
can hardly if at all be implemented in any other way but something like alignas(T) std::byte[sizeof (T)];
? Does the array being in a struct somehow magically make it okay? Why?
Let's say I have
template <typename T>
using uninitialized = alignas(T) std::byte[sizeof (T)];
constexpr auto N = 5;
uninitialized<int> array[N];
for (std::size_t i = 0; i < N; i++) {
new (&array[i]) int(i);
}
Now what? Isn't any cast like
auto laundered_value_ptr = std::launder(reinterpret_cast<int*>(&array[i]));
identical to the first example? What about this:
auto laundered_array_ptr = std::launder(reinterpret_cast<int*>(array));
laundered_array_ptr[0] = 9;
laundered_array_ptr[2] = 76;
If I follow, it seems like there's no way to use this memory correctly, because using a std::byte(*)[sizeof (int)]
means that basically anything around it is reachable, and following first example, everything written in this question is may aswell be UB.
I compiled these examples using g++ -g -O0 -Wextra -Wall -Wpedantic -std=c++20 -fsanitize=undefined -fsanitize=address
and curiously had gotten not even a warning which leaves me completely stumped.
I doubt this matters at all, but here's the version of compiler that I am using.
g++ (GCC) 12.1.1 20220730