std :: bit_cast与std :: array

时间:2019-10-10 09:59:40

标签: c++ c++20 type-punning

在最近的演讲“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的字节吗?

3 个答案:

答案 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]/1-3

  

头文件<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中该浮点数的十六进制表示形式。