When does std::as_writable_bytes trigger undefined behavior

102 Views Asked by At

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)?

1

There are 1 best solutions below

1
Artyer On

b == 0 in g() is asking if b == nullptr since b is an array (which the compiler optimizes to false).

Also your function h() should have std::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 b might 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:

int h() {
    bool b{true};
    std::span<bool> bs(&b, 1);

    auto ps = std::as_writable_bytes(bs);

    bool f{false};
    std::span<bool> fs(&f, 1);
    std::ranges::copy(std::as_bytes(fs), ps.begin());

    return b == false;
}