Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::bit_cast with std::array

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?

like image 628
Evg Avatar asked Oct 10 '19 09:10

Evg


People also ask

What is the difference between std :: array and array?

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.

Is std :: array useful?

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.

Does std :: array initialize?

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.

Is std :: array pod?

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.


2 Answers

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 chars 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.

like image 69
Timur Doumler Avatar answered Oct 11 '22 15:10

Timur Doumler


The accepted answer is incorrect because it fails to consider alignment and padding issues.

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 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.

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.

like image 27
L. F. Avatar answered Oct 11 '22 15:10

L. F.