Bonjour à tous !
Après un petit mois d’absence, je vous propose aujourd’hui un petit article traitant d’une nouveauté à venir de C++20 : std::bit_cast.
reinterpret_cast ?
reinterpret_cast est l’un des opérateurs de conversion en C++. Celui-ci est assez mal compris par les développeurs débutants qui s’attendent à pouvoir faire des choses telles que :
1 2 3 4 5 |
auto i = new std::int64_t(45678); auto d = reinterpret_cast<double*>(&i); std::cout << i << '\n'; std::cout << *d << '\n'; |
Si ce code peut sembler correct, il ne l’est pas. Il s’agit ici d’un undefined behavior résultant des règles de strict pointer aliasing (notez que cette règle est aussi valide pour les unions contrairement au C).
Il existe un certain nombre d’opérations définies par le standard qu’il est possible de faire avec reinterpret_cast. Dans l’exemple ci-dessus, la seule opération définie sur d est de le reconvertir en son type initial, à savoir std::int64_t*. Déréférencer d est un comportement indéterminé. Je vous laisse d’ailleurs une citation de la doc de MSDN :
The result of a reinterpret_cast cannot safely be used for anything other than being cast back to its original type. Other uses are, at best, nonportable.
Alors comment faire ? La solution standard avant C++20 est d’utiliser std::memcpy :
1 2 |
double d; std::memcpy(&d, &i, sizeof(std::int64_t)); |
C’est assez lourd à l’écriture et en plus, std::memcpy n’est pas constexpr, ce qui empêche de faire ce genre de conversion à la compilation. Notez ici qu’on a la nécessité d’avoir un type de destination qui est default-constructible, ou tout du moins de le construire avant l’appel de std::memcpy.
std::bit_cast
La p0476 propose donc std::bit_cast qui permettrait de simplifier ce genre de manipulation.
Notez d’ailleurs que std::bit_cast est une fonction de la bibliothèque standard et non pas un nouvel opérateur de conversion. Elle est définie dans le nouveau header <bit>.
std::bit_cast n’est pour le moment supporté par aucun compilateur, notamment parce qu’il n’est pas possible d’en implémenter une version constexpr sans que le compilateur fasse un traitement spécifique. Cela étant, elle permet d’effectuer des réinterprétations de séquences de bits sans tomber dans l’undefined behavior :
1 2 3 4 |
constexpr std::int64_t i = 299430; constexpr auto d = std::bit_cast<double>(i); std::cout << d << std::endl; |
Cette fonction n’est utilisable (et ne participe à la résolution de surcharge) que si la taille du type d’entrée est égale à celle du type de sortie et que les deux types sont copiables de façon triviale (TriviallyCopyable).
Voilà donc pour cet ajout mineur à venir pour C++20. Je vous dis à bientôt pour un nouvel article :).
You must log in to post a comment.