In his recent talk “Type punning in modern C++” Timur Doumler said that std::bit_cast
cannot be used to bit cast a float
into an unsigned char[4]
because C-style arrays cannot be returned from a function. We should either use std::memcpy
or wait until C++23 (or later) when something like reinterpret_cast<unsigned char*>(&f)[i]
will become well defined.
In C++20, can we use an std::array
with std::bit_cast
,
float f = /* some value */;
auto bits = std::bit_cast<std::array<unsigned char, sizeof(float)>>(f);
instead of a C-style array to get bytes of a float
?
std::array is just a class version of the classic C array. That means its size is fixed at compile time and it will be allocated as a single chunk (e.g. taking space on the stack). The advantage it has is slightly better performance because there is no indirection between the object and the arrayed data.
What are the advantages of using std::array over usual ones? It has friendly value semantics, so that it can be passed to or returned from functions by value. Its interface makes it more convenient to find the size, and use with STL-style iterator-based algorithms.
std::array contains a built-in array, which can be initialized via an initializer list, which is what the inner set is. The outer set is for aggregate initialization.
In C++03, POD was defined in terms of aggregate: a class where every subobject is native or an aggregate is POD. So, by backwards compatibility, a C++0x std::array is POD.
Yes, this works on all major compilers, and as far as I can tell from looking at the standard, it is portable and guaranteed to work.
First of all, std::array<unsigned char, sizeof(float)>
is guaranteed to be an aggregate (https://eel.is/c++draft/array#overview-2). From this follows that it holds exactly a sizeof(float)
number of char
s inside (typically as a char[]
, although afaics the standard doesn't mandate this particular implementation - but it does say the elements must be contiguous) and cannot have any additional non-static members.
It is therefore trivially copyable, and its size matches that of float
as well.
Those two properties allow you to bit_cast
between them.
Per [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.
The standard does not actually require std::array
to have exactly one public data member of type T[N]
, so in theory it is possible that sizeof(To) != sizeof(From)
or is_trivially_copyable_v<To>
.
I will be surprised if this doesn't work in practice, though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With