我知道这是一个很普通的主题,但是尽管很容易找到典型的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
)
pixels[some_index]
还是*(pixels + something)
?那将是UB。
Pixel
放置的Pixel
s 仍然不是s 的数组。