我以为我终于理解了
reinterpret_cast
和严格别名,然后我遇到了这个示例,该示例是根据 https://en.cppreference.com/w/cpp/language/ub 上的“无效标量”示例稍作修改的
https://godbolt.org/z/jdY8brGha
#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;
}
int h() {
bool b{true};
std::span<bool> bs(&b, sizeof(bool));
auto ps = std::as_writable_bytes(bs);
ps[0] = std::byte{10};
return b == 0;
}
从 godbolt 链接我可以看到 g
函数给出了我期望的汇编输出,而
h
则没有。我还了解到,当我从字节指针写入
b
函数中的
h
时,它的生命周期就会结束(类似于联合的工作方式),从而导致未定义的行为。我还认为
g
函数与此处的 as_bytes 示例类似:https://en.cppreference.com/w/cpp/container/span/as_bytes 我的问题是:
g
函数是否没有未定义的行为?为什么/为什么不?以及可能的后续问题:
如果
g
与
h
功能有同样的问题,
std::as_writable_bytes
的用途是什么以及如何正确使用它?在非数组元素上创建跨度是否非法?这些是否仅特定于 bool (我的测试似乎表明如此,但总是很难用未定义的行为来判断)?
b == 0
中的
g()
询问
b == nullptr
是否为数组,因为
b
是一个数组(编译器将其优化为
false
)。你的函数
h()
应该有
std::span<bool> bs(&b, 1)
,因为你只有1个bool。当前编写的第一个函数不受 UB 的影响,因为写入
b
可能会使其具有陷阱表示,但你从未真正尝试从中读取。但是将其更改为
return b[0] == false;
将使其具有与第二个函数相同的UB。至于你的后续问题,你必须编写一个有效的对象表示:
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;
}