Ответ 1
Для типов, для которых разрешено такое использование (например, если T1
является POD-типом, а T2
- unsigned char
), подход с static_cast
хорошо определен стандартом.
С другой стороны, reinterpret_cast
полностью определяется реализацией - единственная гарантия, которую вы получаете за него, заключается в том, что вы можете использовать тип указателя для любого другого типа указателя, а затем обратно, и вы получите исходное значение; а также вы можете использовать тип указателя для целочисленного типа, достаточно большого, чтобы удерживать значение указателя (которое зависит от реализации и вообще не существует), а затем отбрасывает его, и вы получите исходное значение.
Чтобы быть более конкретным, я просто процитирую соответствующие части Стандарта, выделив важные части:
5.2.10 [expr.reinterpret.cast]:
Отображение, выполненное с помощью reinterpret_cast, реализовано. [Примечание: оно может или не может выдавать представление, отличное от исходного значения.]... Указатель на объект может быть явно преобразован в указатель на объект другого типа.) За исключением того, что преобразование rvalue типа "указатель на T1" на тип "указатель на T2" (где T1 и T2 являются типами объектов и где требования к выравниванию T2 не более строгие, чем требования T1), и обратно к исходному типу дает исходное значение указателя, результат такого преобразования указателя не указан.
Так что-то вроде этого:
struct pod_t { int x; };
pod_t pod;
char* p = reinterpret_cast<char*>(&pod);
memset(p, 0, sizeof pod);
эффективно не указывается.
Объяснение, почему static_cast
работает немного сложнее. Здесь приведенный выше код переписан для использования static_cast
, который, как я полагаю, гарантированно будет всегда работать в соответствии со стандартом:
struct pod_t { int x; };
pod_t pod;
char* p = static_cast<char*>(static_cast<void*>(&pod));
memset(p, 0, sizeof pod);
Опять же, позвольте мне привести разделы Стандарта, которые вместе приведут меня к выводу, что приведенное выше должно быть переносимым:
3,9 [basic.types]:
Для любого объекта (кроме субобъекта базового класса) типа POD T, независимо от того, имеет ли объект допустимое значение типа T, базовые байты (1.7), составляющие объект, могут быть скопированы в массив char или без знака char. Если содержимое массива char или unsigned char будет скопировано обратно в объект, объект должен сохранить первоначальное значение.
Объектное представление объекта типа T представляет собой последовательность N неподписанных char объектов, занятых объектом типа T, где N равно sizeof (T).
3.9.2 [basic.compound]:
Объекты cv-qualified (3.9.3) или cv-unqualified type
void*
(указатель на void) могут использоваться для указания объектов неизвестного типа. Avoid*
должен иметь возможность удерживать любой указатель объекта. Cv-квалифицированный или cv-неквалифицированный (3.9.3)void*
должен иметь те же требования к представлению и выравниванию, что и cv-qualified или cv-unqualifiedchar*
.
3.10 [basic.lval]:
Если программа пытается получить доступ к сохраненному значению объекта через значение l, отличное от одного из следующих типов, поведение undefined):
- ...
- a char или неподписанный char тип.
4.10 [conv.ptr]:
Значение типа "указатель на cv T", где T - тип объекта, может быть преобразовано в rvalue типа "указатель на cv void". Результат преобразования "указателя на cv T" в "указатель на cv void" указывает на начало места хранения, где находится объект типа T, как если бы объект был наиболее производным объектом (1.8) типа T (то есть не подобъектом базового класса).
5.2.9 [expr.static.cast]:
Обратное к любой стандартной последовательности преобразования (раздел 4), отличной от преобразований lvalue-to-rvalue (4.1), array-topointer (4.2), функции-to-pointer (4.3) и boolean (4.12) может выполняться явно с использованием static_cast.
[EDIT] С другой стороны, у нас есть этот драгоценный камень:
9.2 [class.mem]/17:
Указатель на объект POD-структуры, подходящим образом преобразованный с использованием reinterpret_cast, указывает на его начальный член (или если этот элемент является битовым полем, а затем блоку, в котором он находится) и наоборот. [Примечание: может быть , поэтому может быть неназванным заполнением внутри объекта POD-структуры, но не в начале, по мере необходимости, для достижения соответствующего выравнивания. ]
что, по-видимому, означает, что reinterpret_cast
между указателями как-то подразумевает "тот же адрес". Идите фигуру.