类型双关主题的变体:就地琐碎构造
Variation on the type punning theme: in-place trivial construction
我知道这是一个很常见的主题,但是尽管典型的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
数组对象x
n
元素 ([dcl.array](,则表达式P + J
和J + P
(其中J
的值为j
(指向(可能假设的(数组元素i+j
x
如果0≤i+j≤n
和表达式P - J
指向(可能假设的(数组元素i−j
x
如果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
对象适当对齐,所有内容的行为方式都会相同。
- ArduinoJson 6.15.2:JsonObject没有命名类型
- 防止主数据类型C++的隐式转换
- 大量序列中核苷酸类型的快速计数
- 如何从C++中的依赖类型中获得它所依赖的类型
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 是否可以初始化不可复制类型的成员变量(或基类)
- 如何获取std::result_of函数的返回类型
- 从父命名空间重载类型
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- Openssl 1.1.1d无效使用不完整的类型"struct dsa_st"
- 访问者访问变体并返回不同类型时出错
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 模板元程序查找相似的连续类型名称
- 是否可以从int转换为enum类类型
- 构造函数正在调用一个使用当前类类型的函数
- 我应该使用什么来代替void作为变体中的替代类型之一
- 类中的字符串不命名类型