std::bit_cast 和 std::array

std::bit_cast with std::array

在他最近的演讲 “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::arraystd::bit_cast

一起使用吗
float f = /* some value */;
auto bits = std::bit_cast<std::array<unsigned char, sizeof(float)>>(f);

而不是 C 样式数组来获取 float?

的字节

接受的答案不正确,因为它没有考虑对齐和填充问题。

根据 [array]/1-3:

The header <array> defines a class template for storing fixed-size sequences of objects. An array is a contiguous container. An instance of array<T, N> stores N elements of type T, so that size() == N is an invariant.

An array is an aggregate that can be list-initialized with up to N elements whose types are convertible to T.

An array meets all of the requirements of a container and of a reversible container ([container.requirements]), except that a default constructed array object is not empty and that swap does not have constant complexity. An array meets some of the requirements of a sequence container. Descriptions are provided here only for operations on array that are not described in one of these tables and for operations where there is additional semantic information.

标准实际上并不要求 std::array 恰好有一个 public 类型 T[N] 的数据成员,所以理论上 sizeof(To) != sizeof(From)is_­trivially_­copyable_­v<To>

不过,如果这在实践中不起作用,我会感到惊讶。

是的。

根据 paper that describes the behaviour of std::bit_cast, and its 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 charsize_of(float) 的数组尊重所有这些断言,因此将执行基础 std::memcpy。因此,结果数组中的每个元素都是浮点数的一个连续字节。

为了证明这种行为,我在 Compiler Explorer 中编写了一个小示例,您可以在此处尝试:https://godbolt.org/z/4G21zS. The float 5.0 is properly stored as an array of bytes (Ox40a00000) that corresponds to the hexadecimal representation of that float number in Big Endian

是的,这适用于所有主要的编译器,据我看标准可以看出,它是可移植的并且保证可以工作。

首先保证std::array<unsigned char, sizeof(float)>是一个聚合(https://eel.is/c++draft/array#overview-2)。由此可见,它在内部恰好包含 sizeof(float) 数量的 char(通常作为 char[],尽管 afaics 标准并未强制执行此特定实现 - 但它确实说明了元素必须是连续的)并且不能有任何额外的非静态成员。

因此可以轻松复制,并且其大小也与 float 相匹配。

这两个属性允许您在它们之间 bit_cast