Почему Reinterpret_cast Force copy_n не используется для трансляции между одинаковыми размерами?

Согласно cppreference.com, reinterpret_cast:

Преобразует между типами, переинтерпретируя базовый шаблон бит.

Но подождите, что ложь заставит его работать только в этих случаях:

Когда указатель или ссылка на объект типа T1 составляет reinterpret_cast (или приведение в стиле C) к указателю или ссылке на объект другого типа T2, приведение всегда выполняется успешно, но полученный указатель или ссылка может быть доступна только в том случае, если оба типа T1 и T2 являются стандартными типами макетов, и одно из следующих значений истинно:

  • T2 является (возможно, cv-квалифицированным) динамическим типом объекта
  • T2 и T1 - оба (возможно, многоуровневые, возможно, cv-квалификаторы на каждом уровне) указатели на один и тот же тип T3
  • T2 - это (возможно, cv-квалифицированный) подписанный или неподписанный вариант динамического типа объекта
  • T2 - это тип агрегата или тип объединения, который содержит один из вышеупомянутых типов как элемент или нестатический член (включая рекурсивно, элементы субагрегатов и нестатические члены данных объединенных объединений): это делает его безопасным для перевода из первого члена структуры и из элемента объединения в struct/union, который содержит его.
  • T2 является (возможно, cv-квалифицированным) базовым классом динамического типа объекта
  • T2 - char или unsigned char

В соответствии с этим списком незаконным примером может быть:

auto foo = 13LL;
auto bar = reinterpret_cast<double&>(foo);

Таким образом, единственный приемлемый способ сделать это - скопировать память:

auto foo = 13LL;
double bar;

copy_n(reinterpret_cast<char*>(&foo), sizeof(foo), reinterpret_cast<char*>(&bar));

Мой вопрос: почему reinterpret_cast не обрабатывает это для меня? Или есть что-то еще, поэтому мне не нужно прыгать через этот обруч?

Ответ 1

Почему reinterpret_cast не обрабатывает это для меня?

Одна из причин заключается в том, что представления размера, выравнивания и бит не указаны, поэтому такое преобразование не будет переносимым. Тем не менее, это на самом деле не оправдывало бы выполнение поведения undefined, только определяемое реализацией.

Сделав это undefined, компилятору разрешено предположить, что выражения несвязанных типов не имеют доступа к одному и тому же объекту, что может обеспечить лучшую оптимизацию. Например, в следующем:

int   & i = something();
float & f = something_else();

const int i1 = i;
f = 42;
const int i2 = i;

компилятор может предположить, что i1 и i2 имеют одинаковое значение (i не изменяются при назначении на f) и оптимизируют их в одну константу. Прерывание этого предположения приведет к поведению undefined.

Или есть что-то еще, поэтому мне не нужно прыгать через этот обруч?

Копирование байтов - единственный четко определенный способ переинтерпретировать один тип объекта как несвязанный тип.

Слияние с reinterpret_cast или объединение может работать иногда (при условии совпадения размера и т.д.), но может вас отвлечь, если оптимизатор становится слишком умным с поведением undefined.

Ответ 2

В основном ограничения на reinterpret_cast (не полностью захваченные сайтом cppreference) связаны с

  • проблемы с выравниванием и
  • ловушки.

Для гипотетического значения reinterpret_cast для числа, различные размеры могут быть легко обработаны усечением или нулевым расширением, так как каждый из них находится в опасной зоне уровня бит, так что это не проблема.

Используя memcpy или copy_n, вы работаете вокруг проблем выравнивания, но вы по-прежнему можете стать жертвой ловушек. Это означает, что использование итогового значения может взорваться. На некоторых платформах для некоторых значений.


Обратите внимание, что стандарт гарантирует что-либо, может быть и обычно расширяется любым конкретным компилятором.

Часто может быть хорошей идеей стремиться к чуть меньшей переносимости, чем то, что вы можете иметь, полагаясь исключительно на стандарт.

Например, все быстро усложняется, если вы не можете предположить, что байтом является 8 бит. Принятие этого предположения снижает переносимость. Но все же множество поддерживаемых платформ велико.