在最近的演讲“Type punning in modern C++” Timur Doumler said中,std::bit_cast
不能用于将float
强制转换为unsigned char[4]
,因为C样式数组不能从函数返回。我们应该使用std::memcpy
或等到C ++ 23(或更高版本)后才能正确定义reinterpret_cast<unsigned char*>(&f)[i]
。
在C ++ 20中,我们可以将std::array
与std::bit_cast
一起使用吗
float f = /* some value */;
auto bits = std::bit_cast<std::array<unsigned char, sizeof(float)>>(f);
代替C型数组来获取float
的字节吗?
答案 0 :(得分:15)
是的,这适用于所有主要的编译器,据我从标准看,它是可移植的,并且保证可以工作。
首先,保证std::array<unsigned char, sizeof(float)>
是一个聚合(https://eel.is/c++draft/array#overview-2)。由此可见,它在内部恰好包含sizeof(float)
个char
(通常为char[]
),尽管从某种意义上说,该标准并未强制要求该特定实现-但它确实指出了元素(必须是连续的),并且不能有任何其他非静态成员。
因此,它是可复制的,并且其大小也与float
相同。
这两个属性使您可以在它们之间bit_cast
。
答案 1 :(得分:6)
头文件
<array>
定义用于存储固定大小的类模板 对象序列。数组是一个连续的容器。一个实例array<T, N>
中的N
存储了T
类型的size() == N
元素,因此N
是不变的。数组是一个聚合,最多可以用
T
进行列表初始化。 类型可转换为[container.requirements]
的元素。一个数组满足容器和容器的所有要求 可逆容器(
std::array
),但默认 构造的数组对象不为空,并且该交换没有 不断的复杂性。数组满足以下条件 序列容器。此处仅提供操作说明 在这些表之一中未描述的阵列上 还有其他语义信息的操作。
该标准实际上并不要求T[N]
拥有一个类型为sizeof(To) != sizeof(From)
的公共数据成员,因此从理论上讲,is_trivially_copyable_v<To>
或updateOne
是可能的。
但是,如果这在实践中不起作用,我会感到惊讶。
答案 2 :(得分:2)
是的
根据描述std::bit_cast
的行为的paper以及它的proposed implementation,只要这两种类型具有相同的大小并且可以复制即可,强制转换应该成功。
std::bit_cast
的简化实现应类似于:
template <class Dest, class Source>
inline Dest bit_cast(Source const &source) {
static_assert(sizeof(Dest) == sizeof(Source));
static_assert(std::is_trivially_copyable<Dest>::value);
static_assert(std::is_trivially_copyable<Source>::value);
Dest dest;
std::memcpy(&dest, &source, sizeof(dest));
return dest;
}
由于浮点数(4个字节)和带有unsigned char
的{{1}}数组尊重所有这些断言,因此将执行基础的size_of(float)
。因此,结果数组中的每个元素将是float的一个连续字节。
为了证明这种行为,我在Compiler Explorer中写了一个小例子,您可以在这里尝试:https://godbolt.org/z/4G21zS。浮点数5.0正确地存储为字节数组(std::memcpy
,它对应于Big Endian中该浮点数的十六进制表示形式。