I thought I finally understood reinterpret_cast and strict aliasing, and then I came across this example, slightly modified from the "Invalid scalar" example on https://en.cppreference.com/w/cpp/language/ub
https://godbolt.org/z/navGM37h3
#include <span>
int g() {
bool b[1] = {true};
std::span<bool> bs(b);
auto ps = std::as_writable_bytes(bs);
ps[0] = std::byte{10};
return b[0] == 0;
}
int h() {
bool b{true};
std::span<bool> bs(&b, 1);
auto ps = std::as_writable_bytes(bs);
ps[0] = std::byte{10};
return b == 0;
}
From the godbolt link I can see that the g function gives the assembly output I expect, and the h does not. I have also understood that the lifetime of b in the h function ends when I write to it from the byte pointer (similar to how unions work), causing undefined behavior. I also think the g function is similar to the example for as_bytes here: https://en.cppreference.com/w/cpp/container/span/as_bytes
My question then is:
Is the g function free from undefined behavior? Why/why not?
and possible follow up questions:
If g has the same problem as the h function, what is the purpose of std::as_writable_bytes and how do you use it in a correct way? Is it instead illegal to create a span over a non-array element? Is any of this only specific to bool (my testing seems to suggest so, but always hard to tell with undefined behavior)?
b == 0ing()is asking ifb == nullptrsincebis an array (which the compiler optimizes tofalse).Also your function
h()should havestd::span<bool> bs(&b, 1)since you only have 1 bool.Your first function as it is currently written is free from UB, since writing to
bmight make it have a trap representation but you never actually try to read from it.But changing it to
return b[0] == false;will make it have the same UB as the second function.As for your follow up question, you have to write a valid object representation: