Для типов, для которых разрешено такое приведение (например, если 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 или unsigned char копируется обратно в объект, объект впоследствии должен сохранить свое исходное значение.
Объектное представление объекта типа T - это последовательность из N беззнаковых char объектов, занятых объектом типа T, где N равно sizeof (T).
3.9.2 [basic.compound]:
Объекты cv-qualified (3.9.3) или cv-unqualified type void*
(указатель на void) могут использоваться для указания на объекты неизвестного типа. void*
должен иметь возможность удерживать любой указатель на объект. Квалифицированный cv или cv-неквалифицированный (3.9.3) void*
должен иметь те же требования к представлению и выравниванию, что и cv-квалифицированный или cv-неквалифицированный char*
.
3.10 [basic.lval]:
Если программа пытается получить доступ к сохраненному значению объекта через lvalue, отличное от одного из следующих типов, поведение не определено):
- ...
- тип char или unsigned char.
4.10 [conv.ptr]:
R-значение типа «указатель на cv T», где T - тип объекта, может быть преобразовано в r-значение типа «указатель на cv void». Результат преобразования «указателя на cv T» в «указатель на cv void» указывает на начало места хранения, где находится объект типа T, как если бы объект является наиболее производным объектом (1.8) типа T (то есть не подобъект базового класса).
5.2.9 [expr.static.cast]:
Может быть выполнена обратная любая стандартная последовательность преобразования (раздел 4), кроме преобразования lvalue-to-rvalue (4.1), массива-указателя (4.2), функции в указатель (4.3) и логического (4.12) преобразования. явно используя static_cast.
[EDIT] С другой стороны, у нас есть жемчужина:
9.2 [class.mem] / 17:
Указатель на объект POD-структуры, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на его начальный член (или, если этот член является битовым полем, то на модуль, в котором он находится) и наоборот. [Примечание: поэтому может быть безымянное заполнение внутри объекта POD-структуры, но не в его начале, что необходимо для достижения надлежащего выравнивания. ]
что, кажется, подразумевает, что reinterpret_cast
между указателями каким-то образом подразумевает "один и тот же адрес". Иди разберись.
person
Pavel Minaev
schedule
07.12.2009
reinterpret_cast
, которая переписывает ее при использовании с определенными типами в этойstatic_cast
последовательности. - person Johannes Schaub - litb   schedule 08.12.2009