如何将一种类型的多个值合并为另一种类型中的单个值

How to merge several values of one type into a single value of another type

本文关键字:类型 合并 另一种 单个值 一种      更新时间:2023-10-16

E.g给定长度为100的vector<uint8>,如何创建50个元素的新vector<uint16>。最好不要复制?

(编辑:我的评论信息(

举例说明:

我有一个uint16灰度图像文件,我的第三方lib返回uint8的向量。每2个字节=1个像素。对我来说,使用uint16的向量是实用的。我认为这个vector<uint8>和相应的vector<uint16>之间的唯一区别是,字节是以"级联"的方式读取的(即,2个字节的块=1个值(。

我可以循环并将每2个元素组合成一个新的向量元素,但这似乎效率很低,因为内存布局是相同的。我希望我可以结合一些强制转换和移动构造函数来创建一个vector<uint16>——而不需要再次复制原始的vector<uint8>字节。

编辑2:为了消除任何可能的误解,我画了一幅画,请原谅我糟糕的ascii艺术:(

内存中uint8值的容器:

[_]|[_]|[_]
|^^|
访问元素=访问1字节

内存中uint16值的容器也是一个字节序列;

[_]|[_]|[_]

|^^^^|
访问元素=访问2个字节(比如我的系统将其读取为big-endian(

我已经有了向量v1中的字节序列。我只想要一个不同类型的v2,这样我就可以以不同的方式访问它们。如果结果是uint16值被读取为little-endian,我仍然可以使用它。

编辑3:到目前为止,布莱克的答案似乎是我所理解的最好的(如果没有任何变化,我稍后会接受(,但没有简单或STL的解决方案似乎仍然很奇怪。尽管如此,还是要感谢大家对我解释自己的及时投入和耐心。

由于您不控制源(根据注释(,您无法知道输入向量有一个2字节对齐的缓冲区。仅出于这个原因,您就必须复制输入向量。

你怎么做并不重要;内存访问速度可能会主导运行时间。但是,请在目标向量上调用reserve(50)-具有多个分配减慢程序速度。

您可以编写一个包装器,以便在需要时为您进行转换。例如(无模板(

static inline uint16 getElement(const vector<uint8> &p, size_t index) {
  const int idx = index * 2;
  return p[idx] | p[idx + 1] << 8;
}

vector<std::uint8_t>将保持一个std::uint8_t*,而vector<std::uint16_t>将保持std::uint16_t*。你想做的基本上是分享这两个指针,并给出不同的解释,比如

auto ptr = reinterpret_cast<std::uint16_t*>( vectorOfUint8.data() )

只要您不通过该指针读/写,这是可以的,因为由于严格的别名规则,这样的操作将导致未定义的行为。您需要进行复制,使用SIMD可以对其进行优化,使其非常高效。如果你的编译器不能自动完成,你可以使用内部函数或展开它:

  1. 从v1到v2读取两个元素,使它们手动获得96(v2中为48(
  2. 在v2上循环并一次读取4个(i+=48/4=12(或8个(i+-=48/8=6(元素

您也可以禁用严格的别名规则,尽管这是编译器特定的,因此不是标准的和可移植的。

您必须进行类型转换,在这种情况下,我认为您不会在不复制的情况下进行。

矢量是一个连续的内存块,uint8将导致所有数字占据8位,并且在内存中相邻。现在,当您将其转换为uint16时,每个数字都需要额外的8位,并且您无法神奇地在连续块之间插入内存。因此,复制将发生。

如果没有不复制的限制,这个问题并不难,我相信你可以解决。

编辑:作为对注释的回应,即使向量有100个元素的空间,但其中的元素少于50个,类型转换仍然需要复制。前两个元素将占用16位,而稍后第一个元素将占据这16位。因此,您必须至少复制第二个元素。在类似的逻辑中,您将不得不复制其他元素。