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::array
与 std::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 char
和 size_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
。
在他最近的演讲 “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
?
接受的答案不正确,因为它没有考虑对齐和填充问题。
根据 [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 ofarray<T, N>
storesN
elements of typeT
, so thatsize() == N
is an invariant.An array is an aggregate that can be list-initialized with up to
N
elements whose types are convertible toT
.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 char
和 size_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
。