Для типов, для которых такое приведение допускается (например, если 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 [основной.Типы]:
Для любого объекта (кроме базового класса подобъекта) типа СТРУЧКА T, имеет ли или нет объекта действительного значения типа T, лежащие в основе байты (1.7), составляющие объект может быть скопировано в массив char или unsigned char. Если содержимое массива char или unsigned char копируется обратно в объект, объект должен впоследствии сохранить свое первоначальное значение.
Объектное представление объекта типа T представляет собой последовательность N unsigned char объектов, занимаемых объектом типа T, где N равно sizeof (T).
3.9.2 [basic.compound]:
Объекты CV-квалифицированные (3.9.3) или CV-неквалифицированного типа void*
(указатель к мочеиспусканию), может быть использован, чтобы указать на объекты неизвестного типа. A void*
должен иметь возможность удерживать любой указатель объекта. Cv-квалифицированный или cv-неквалифицированный (3.9.3) void*
должен иметь те же требования к представлению и выравниванию, что и cv-qualified или cv-unqualified char*
.
3,10 [basic.lval]:
Если программа пытается получить доступ к сохраненному значению объекта через именующий, отличную от одного из следующих типов поведение не определенно):
- ...
- полукокса или тип без знака символ.
4,10 [conv.ptr]:
Rvalue типа «указатель на сорта Т», где Т представляет собой тип объекта, может быть преобразован в RValue типа «указатель cv void. «Результат преобразования« указателя на cv T »в« указатель на cv void »указывает на начало места хранения, где находится объект типа T, как если бы объект был наиболее производным объектом (1.8) типа T (т. е. не подобъект базового класса).
5.2.9 [expr.static.cast]:
Обратные любой стандартной последовательности преобразования (пункт 4), отличные от именующего-к-Rvalue (4.1), массив-topointer (4.2), функция-to-pointer (4.3) и логические (4.12) преобразования могут выполняться явно с использованием static_cast.
[EDIT] С другой стороны, у нас есть этот драгоценный камень:
9,2 [class.mem]/17:
Указатель на объект POD-структуры, соответственно преобразованный с использованием reinterpret_cast, указывает на его начальный член (или если этот элемент является битовым полем, а затем на единицу, в которой он находится) и наоборот.[Примечание: может быть поэтому быть неназванным дополнением в объекте POD-struct, но не в его начале, при необходимости для достижения соответствующего выравнивания. ]
, который, по-видимому, подразумевает, чтомежду указателями как-то подразумевает «тот же адрес». Идите фигуру.
Спасибо, что спросили об этом. Кажется, что ответ полон в Стандарте. (несколько раз спросил его в usenet, никто не мог указать на гарантию того, что эта последовательность статического литья делает лучше). Там * есть *, чтобы быть чем-то: C++ 0x добавил некоторые формулировки спецификации 'reinterpret_cast', которая перезаписывает его при использовании с определенными типами в этой последовательности' static_cast'. –
@sinec Я должен спросить, почему вы считаете необходимым выполнять такие приведения? На протяжении многих лет я написал множество нагрузок кода C++, не требуя этого. – 2009-12-07 21:53:50
@Neil Butterworth Я работал над проектом (это было больше похоже на добавление новых функций в существующий код), и было много сумасшедших бросков, но это было неизбежно, поскольку мы не могли изменить устаревший код. Во всяком случае, я задал этот вопрос, так как я читаю книгу, и я не мог найти объяснения этого. – sinek