В данном коде демонстрируется типичная ошибка подмены типа (type punning [
?]), ведущая к неминуемому UB [
?] по стандарту.
num в данном случае представляет собой шестнадцатеричное число, при выводе оно переводится в десятичное число типа double.
Вот что стандарт говорит о подмене типа.
[
basic.lval#11]
If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:
(11.1) -- the dynamic type of the object,
(11.2) -- a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
(11.3) -- a char, unsigned char, or std::byte type.
Относительно
unsigned long long
тип
double
не обладает ни одним из требуемых стандартом свойств. Отсюда прямой вывод что данный код вносит UB.
Поэтому под большим вопросом остается то, что выводится данным кодом.
В итоге, правильным примером подмены типа на другой может быть только пример, где используется подмена на разрешенный тип представления объекта:
char
,
unsigned char
или
std::byte
для C++17.
C++ Core Guidelines в секции
C.183 однозначно не советует для подмены использовать
union
. Этого действительно не стоит делать даже при условии того, что все современные трансляторы понимают такие конструкции так, как подозревает писатель.
Почему неверно работает, к примеру, такой фрагмент кода:
Второй блок кода означает приведение значения из типа
unsigned long long
в тип
double
. В этом случае производится именно стандартное преобразование типа, а не подмена с целью интерпретации представления памяти
num
иным образом.
0xC068C0C000000000
для типа
unsigned long long
означает
13864543383726325760
, при преобразовании этого значения в тип
double
значение будет преобразовано в
1.3864543383726326e+19
. В таком виде оно и будет выведено.
Относительно первого блока кода, лучшим примером подмены типа для кода из вопроса будет такой:
unsigned long long num = 0xC068C0C000000000;
double representation = {};
memcpy( &representation, &num, sizeof( num ) );
cout << representation;
И не стоит бояться обращения к
memcpy
тут. Данный код полностью соответствует стандарту и ожидаемо оптимизируется транслятором до
желаемого результата.
Мне не понятно, как работает такая конструкция, т.е зачем передавать именно ссылку на переменную num?
Иногда требуется произвести приведение типа значения [
?], иногда требуется произвести интерпретацию памяти этого значения другим типом. Это и называется подменой типа или алиасингом. Более детальную информацию о подмене типа, и алиасинге вообще, можно узнать
из этой заметки.