类型双关主题的变体:就地琐碎构造

Variation on the type punning theme: in-place trivial construction

本文关键字:类型      更新时间:2023-10-16

我知道这是一个很常见的主题,但是尽管典型的UB很容易找到,但到目前为止我还没有找到这个变体。

因此,我正在尝试正式引入Pixel对象,同时避免数据的实际副本。

这有效吗?

struct Pixel {
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha;
};
static_assert(std::is_trivial_v<Pixel>);
Pixel* promote(std::byte* data, std::size_t count)
{
Pixel * const result = reinterpret_cast<Pixel*>(data);
while (count-- > 0) {
new (data) Pixel{
std::to_integer<uint8_t>(data[0]),
std::to_integer<uint8_t>(data[1]),
std::to_integer<uint8_t>(data[2]),
std::to_integer<uint8_t>(data[3])
};
data += sizeof(Pixel);
}
return result; // throw in a std::launder? I believe it is not mandatory here.
}

预期使用模式,大大简化:

std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data

更具体地说:

  • 此代码是否具有明确定义的行为?
  • 如果是,使用返回的指针是否安全?
  • 如果是,可以扩展到哪些其他Pixel类型?(放宽is_trivial限制?像素只有3个组件?(。

clang和gcc都优化了整个循环到虚无,这就是我想要的。现在,我想知道这是否违反了一些C++规则。

Godbolt链接,如果你想玩它。

(注意:尽管有std::byte,我没有标记c ++ 17,因为这个问题使用char成立(

promote的结果用作数组是未定义的行为。 如果我们看一下 [expr.add]/4.2,我们有

否则,如果P指向数组元素i数组对象xn元素 ([dcl.array](,则表达式P + JJ + P(其中J的值为j(指向(可能假设的(数组元素i+jx如果0≤i+j≤n和表达式P - J指向(可能假设的(数组元素i−jx如果0≤i−j≤n.

我们看到它需要指针实际指向数组对象。 不过,您实际上没有数组对象。 您有一个指向单个Pixel的指针,该指针恰好在连续内存中具有其他Pixels跟随它。 这意味着您实际可以访问的唯一元素是第一个元素。 尝试访问任何其他内容将是未定义的行为,因为您已经过了指针的有效域的末尾。

您已经对返回指针的有限使用有了答案,但我想补充一点,我也认为您需要std::launder才能访问第一个Pixel

reinterpret_cast是在创建任何Pixel对象之前完成的(假设您没有在getSomeImageData中这样做(。因此reinterpret_cast不会更改指针值。生成的指针仍将指向传递给函数的std::byte数组的第一个元素。

创建Pixel对象时,它们将嵌套std::byte数组中,std::byte数组将为Pixel对象提供存储

在某些情况下,重用存储会导致指向旧对象的指针自动指向新对象。但这不是这里发生的事情,所以result仍然会指向std::byte对象,而不是Pixel对象。我想使用它就好像它指向一个Pixel对象一样在技术上将是未定义的行为。

我认为即使您在创建Pixel对象后执行reinterpret_cast,这仍然成立,因为Pixel对象和为其提供存储的std::byte不是指针可相互转换的。所以即使这样,指针也会一直指向std::byte,而不是Pixel对象。

如果您获得了从其中一个放置 new 的结果返回的指针,那么就对该特定Pixel对象的访问而言,一切应该都正常。


此外,您还需要确保std::byte指针为Pixel适当对齐,并且数组确实足够大。据我所知,该标准并不真正要求Pixel具有与std::byte相同的对齐方式或没有填充。


此外,这些都不取决于Pixel是微不足道的或它的任何其他属性。只要std::byte数组具有足够的大小并针对Pixel对象适当对齐,所有内容的行为方式都会相同。