Tôi biết đây là một chủ đề khá phổ biến, nhưng nhiều như UB thông thường rất dễ tìm, cho đến nay tôi không tìm thấy biến thể này.
Vì vậy, tôi đang cố gắng chính thức giới thiệu các đối tượng Pixel trong khi tránh một bản sao thực sự của dữ liệu.
Điều này có hợp lệ không?
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.
}
Mô hình sử dụng dự kiến, rất đơn giản:
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
Cụ thể hơn:
- Liệu mã này có hành vi được xác định rõ?
- Nếu có, nó có an toàn khi sử dụng con trỏ trả về không?
- Nếu có, những
Pixel
loại khác nó có thể được mở rộng? (nới lỏng hạn chế is_trivial? pixel chỉ với 3 thành phần?).
Cả clang và gcc đều tối ưu hóa toàn bộ vòng lặp thành hư vô, đó là điều tôi muốn. Bây giờ, tôi muốn biết liệu điều này có vi phạm một số quy tắc C ++ hay không.
Godbolt liên kết nếu bạn muốn chơi xung quanh nó.
(lưu ý: Tôi không gắn thẻ c ++ 17 mặc dù std::byte
vì câu hỏi vẫn sử dụng char
)
pixels[some_index]
hoặc *(pixels + something)
? Đó sẽ là UB.
pixels
(P) không phải là một con trỏ tới đối tượng mảng mà là một con trỏ tới một đối tượng Pixel
. Điều đó có nghĩa là bạn chỉ có thể truy cập pixels[0]
hợp pháp.
Pixel
s tiếp giáp được đặt mới vẫn không phải là một mảng củaPixel
s.