Для типов, для которых разрешено такое приведение (например, если T1
является POD-типом, а T2
- unsigned char
), подход с static_cast
четко определен Стандартом.
С другой стороны, reinterpret_cast
полностью определяется реализацией - единственная гарантия, которую вы получите за это, - то, что вы можете привести тип указателя к любому другому типу указателя, а затем обратно, и вы получите исходное значение ; а также, вы можете привести тип указателя к целочисленному типу, достаточно большому, чтобы содержать значение указателя (которое варьируется в зависимости от реализации и вообще не должно существовать), а затем привести его обратно, и вы получите исходное значение.
Чтобы быть более конкретным, я просто процитирую соответствующие части Стандарта, выделив важные части:
5.2.10 [expr.reinterpret.cast]:
Отображение, выполняемое reinterpret_cast, определяется реализацией . [Примечание: он может или не может создавать представление, отличное от исходного значения.] ... Указатель на объект может быть явно преобразован в указатель на объект другого типа.) За исключением преобразования значения типа «Указатель на 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 или unsigned char. Если содержимое массива char или unsigned char копируется обратно в объект, объект должен впоследствии сохранять свое первоначальное значение.
Представление объекта для объекта типа T представляет собой последовательность из N знаков без знака объектов , занятых объектом типа T, где N равно sizeof (T).
3.9.2 [basic.compound]:
Объекты cv-квалифицированного (3.9.3) или cv-неквалифицированного типа void*
(указатель на void) могут использоваться для указания на объекты неизвестного типа. void*
должен содержать любой указатель объекта. Квалифицированный по cv или неквалифицированный по cv (3.9.3) void*
должен иметь те же требования к представлению и выравниванию, что и cv-квалифицированный или cv-неквалифицированный
3,10 [basic.lval]:
Если программа пытается получить доступ к сохраненному значению объекта через значение lvalue, отличное от одного из следующих типов, поведение не определено):
- ...
- тип char или unsigned char .
4,10 [conv.ptr]:
Значение типа «указатель на cv T», где T - тип объекта, может быть преобразовано в значение типа «указатель на 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), function-to-pointer (4.3) и boolean (4.12), может быть выполнен явно с использованием static_cast.
[EDIT] С другой стороны, у нас есть этот драгоценный камень:
9.2 [class.mem] / 17:
Указатель на PОбъект OD-struct, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на его начальный элемент (или, если этот элемент является битовым полем, затем на модуль, в котором он находится), и наоборот. [Примечание: может , поэтому может быть безымянным заполнением внутри объекта POD-struct, но не в его начале, что необходимо для достижения соответствующего выравнивания. ]
, что, по-видимому, означает, что reinterpret_cast
между указателями так или иначе подразумевает "один и тот же адрес". Пойди разберись.