【发布时间】:2020-05-01 09:59:17
【问题描述】:
我知道这是一个很常见的主题,但尽管很容易找到典型的 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 link 如果你想玩它。
(注意:尽管有std::byte,但我没有标记c++17,因为问题使用char)
【问题讨论】:
-
但是连续的
Pixels 放置新的仍然不是Pixels 的数组。 -
@spectras 但这并没有创建一个数组。你只有一堆相邻的像素对象。这与数组不同。
-
那么你在哪里做
pixels[some_index]或*(pixels + something)?那将是UB。 -
相关部分是here,关键短语是如果P指向数组对象x的数组元素i。这里
pixels(P) 不是指向数组对象的指针,而是指向单个Pixel的指针。这意味着您只能合法访问pixels[0]。 -
你想看wg21.link/P0593。
标签: c++ language-lawyer reinterpret-cast object-lifetime placement-new