【发布时间】:2021-10-14 17:28:47
【问题描述】:
struct header
{
int a1;
int a2;
// ...;
std::byte * get_data_bytes()
{
return align_up<data>( // make sure alignment requirements are met
reinterpret_cast<std::byte *>(this) + sizeof(*this));
// maybe std::launder around the reinterpret_cast (only) is needed?
}
data & get_data()
{
return *std::launder(reinterpret_cast<data *>(get_data_bytes()));
}
void use_data()
{
get_data().use();
}
};
void example()
{
alignas(header) std::byte storage[/* plenty of space*/];
auto h = new (storage) header;
new (h->get_data_bytes()) data;
h->use_data(); // Does this eventually cause a UB?
}
如果没有 UB,这可能吗?如果没有,是否有替代方案? 要求是数据不是标头的子对象,并且有 没有指向标头数据的指针/引用,以避免额外的 间接性。这也许可以通过灵活的空数组实现,但我 不要认为这些在标准中。
【问题讨论】:
-
你能再澄清一下
data的类型要求吗?我认为你在这里绕着混叠违规跳舞。 -
@AndyG 我故意没有提供
data的任何细节,但可以从一个包含几个整数的普通类型开始讨论。 -
这叫“射自己的脚”。获取结构的起始地址,添加您要访问它的项目的偏移量,使用 C-Style cast 将地址(指针)转换为您想要的。完全未定义的行为。请记住,您可能必须在偏移计算中考虑填充。
-
@CppNerd13373 AFAIK 不,问题是在一般意义上您正在尝试进行结构破解。问题是编译器可以通过
sizeof在数组中放置比对象实际需要的更多的填充。这意味着即使该对象有一个sizeof 12,它也可以对齐到 16 等。如果你好奇的话,上面有一个 CPPCon 视频 -
@ThomasMatthews 尽管这个例子看起来有人想在脚上开枪,但在与低级代码或硬件通信时,几乎所有地方都必须这样做。它通常被布置为最后带有空数组的结构,以便能够在您的标头对象之后访问任意数据。如果您确定这是未定义的行为,请您解释原因并提供替代方法吗?