И static_cast
, и reinterpret_cast
, похоже, отлично подходят для преобразования void*
в другой тип указателя. Есть ли веская причина отдавать предпочтение одному другому?
Должен ли я использовать static_cast или reinterpret_cast при приведении void * к чему угодно
Ответы (8)
Используйте static_cast
: это самое узкое приведение, которое точно описывает, какая конверсия здесь выполняется.
Существует заблуждение, что использование reinterpret_cast
было бы лучше, потому что это означает «полностью игнорировать безопасность типов и просто приводить от A к B».
Однако на самом деле это не описывает эффект reinterpret_cast
. Напротив, reinterpret_cast
имеет ряд значений, для всех из которых утверждается, что «отображение, выполняемое reinterpret_cast
, определяется реализацией». [5.2.10.3]
Но в частном случае преобразования от void*
к T*
отображение полностью четко определено стандартом; а именно, присвоить тип безтиповому указателю без изменения его адреса.
Это повод предпочесть static_cast
.
Вдобавок, что, возможно, более важно, это тот факт, что каждое использование reinterpret_cast
совершенно опасно, потому что оно действительно преобразует что-либо во что-нибудь еще (для указателей), в то время как static_cast
гораздо более ограничительно, что обеспечивает лучший уровень защиты. Это уже избавило меня от ошибок, когда я случайно пытался преобразовать один тип указателя в другой.
static_cast
больше подходит для преобразования void*
в указатель другого типа.
static_cast
- это тип выбора, когда существует естественное, интуитивно понятное преобразование между двумя типами, которое не обязательно работает во время выполнения. Например, вы можете использовать static_cast
для преобразования указателей базового класса в указатели производного класса, что является преобразованием, которое имеет смысл в некоторых случаях, но не может быть проверено до времени выполнения. Точно так же вы можете использовать static_cast
для преобразования из int
в char
, что хорошо определено, но может вызвать потерю точности при выполнении.
reinterpret_cast
, с другой стороны, представляет собой оператор приведения, предназначенный для выполнения преобразований, которые принципиально небезопасны или не переносимы. Например, вы можете использовать reinterpret_cast
для преобразования из void *
в int
, что будет работать правильно, если в вашей системе есть sizeof (void*)
sizeof (int)
. Вы также можете использовать reinterpret_cast
для преобразования float*
в int*
или наоборот, что зависит от платформы, потому что конкретные представления float
s и int
s не обязательно будут иметь что-либо общее между собой.
Короче говоря, если вы когда-нибудь обнаружите, что выполняете преобразование, в котором приведение логически значимо, но не обязательно может быть успешным во время выполнения, избегайте reinterpret_cast
. static_cast
- хороший выбор, если вы заранее знаете, что приведение типов будет работать во время выполнения, и сообщаете компилятору: «Я знаю, что это может не сработать, но, по крайней мере, это имеет смысл, и у меня есть основания полагать, что это сработает. правильно делать правильные вещи во время выполнения ". Затем компилятор может проверить, выполняется ли приведение между связанными типами, сообщая об ошибке времени компиляции, если это не так. Использование reinterpret_cast
для этого с преобразованием указателя полностью обходит проверку безопасности во время компиляции.
Есть несколько обстоятельств, при которых вы можете захотеть использовать dynamic_cast
вместо static_cast
, но они в основном связаны с приведением типов в иерархии классов и (только в редких случаях) имеют непосредственное отношение к void*
.
Что касается того, какой из них предпочтителен в спецификации, ни один из них не упоминается чрезмерно как «правильный для использования» (или, по крайней мере, я не помню, чтобы один из них упоминался таким образом). Однако я думаю, что спецификация требует, чтобы вы используйте static_cast
вместо reinterpret_cast
. Например, при использовании приведения в стиле C, как в
A* ptr = (A*) myVoidPointer;
Порядок используемых операторов приведения всегда пытается использовать static_cast
перед reinterpret_cast
, что является желаемым поведением, поскольку reinterpret_cast
не гарантируется переносимостью.
static_cast
преобразовали базовый тип в производный тип, он будет работать во время выполнения (т. Е. Вы не получите исключение или указатель NULL
), но результат может быть указывая на неправильную ячейку памяти, если задействовано множественное наследование. (Дополнительные сведения см. В этом ответе.) Только dynamic_cast
выполнит проверку времени выполнения (с использованием RTTI) и корректно завершится с ошибкой, если приведение является недействительным.
- person andrewtc; 10.08.2014
Это сложный вопрос. С одной стороны, Конрад отлично замечает определение спецификации для reinterpret_cast, хотя на практике он, вероятно, делает то же самое. С другой стороны, если вы выполняете приведение типов указателей (что довольно часто встречается при индексировании в памяти с помощью символа *, например), static_cast сгенерирует ошибку компилятора, и вы будете вынуждены в любом случае использовать reinterpret_cast.
На практике я использую reinterpret_cast, потому что он лучше описывает цель операции приведения. Вы, конечно, могли бы привести аргумент в пользу того, чтобы другой оператор назначал только переинтерпретацию указателя (что гарантировало возвращение того же адреса), но в стандарте такого нет.
reinterpret_cast
!
- person curiousguy; 19.12.2011
Вероятно, вы получили это void*
с неявным преобразованием, поэтому вам следует использовать static_cast
, потому что он ближе всего к неявному преобразованию.
Используйте для этого static_cast
. Только в самых редких случаях, когда другого выхода нет, используйте reinterpret_cast
.
Трансляция в void*
и обратно с использованием static_cast
и reinterpret_cast
идентична. См. Ответ на ссылке. Но обычно static_cast
предпочтительнее, потому что это более узкое и в целом (но не в этом конкретном случае) более безопасное преобразование.
Я предлагаю всегда использовать самый слабый слепок.
reinterpret_cast
может использоваться для приведения указателя к float
. Чем больше повязка разрушает структуру, тем большего внимания требует ее использование.
В случае char*
, я бы использовал приведение в стиле c, пока у нас не будет reinterpret_pointer_cast
, потому что он слабее и ничего другого недостаточно.
void* p = 0; float f = reinterpret_cast<float>(p);
генерирует error: invalid cast from type ‘void*’ to type ‘float’
. Я могу привести к int, но не с плавающей точкой.
- person Oscar Korz; 30.12.2011
float f = *reinterpret_cast<const float*>(&p);
- person Ben Voigt; 07.08.2013
p
как float
. Для этого используется указатель на p
, но p
принудительно устанавливается на float
, а не на float*
.
- person Ben Voigt; 01.06.2016
reinterpret_cast
преобразуется из некоторого указателя (что бы ни было p
) в const float*
. Затем этот указатель разыменовывается, чтобы получить значение с плавающей запятой. Это действительно приводит к тому, что битовое представление p
используется как float
, но сам reinterpret_cast
по-прежнему приводит тип указателя к другому типу указателя.
- person Cemafor; 16.06.2016
float*
. От указателя к тому, что такое p - двойной указатель.
- person Ben Voigt; 16.06.2016
reinterpret_cast
, является указателем на любой тип p
. Если p
- это double
, это будет преобразование от double*
к const float*
. Тип &p
- double*
.
- person Cemafor; 16.06.2016
*
перед reinterpret_cast
не является частью актерского состава. Это оператор почтения, ответивший на результат приведения. В этом коде reinterpret_cast
преобразует указатель в указатель. Затем последний указатель разыменовывается после завершения приведения.
- person M.M; 26.07.2018
*
- это отдельный оператор, но мой комментарий по-прежнему верен. Вы пропустили дополнительный &
в операнде reinterpret_cast
?
- person Ben Voigt; 26.07.2018
reinterpret_cast<const float *>(&p)
преобразует указатель в указатель. Содержимое < >
- это тип результата приведения.
- person M.M; 27.07.2018
*
преобразует const float *
в float
(оператор приведения не преобразуется в float
)
- person M.M; 27.07.2018
*reinterpret_cast<const float*>(&p)
, действительно приводит p
к float
. Мой комментарий не ошибочен.
- person Ben Voigt; 27.07.2018
float
, что неверно. Выражение преобразует void **
в const float *
, а затем использует операцию разыменования (которая НЕ является преобразованием) для преобразования const float *
в float
.
- person M.M; 27.07.2018
void*
в float*
, что было неверно. Он приводил void**
к float*
, чтобы преобразовать void*
в float
.
- person Ben Voigt; 27.07.2018
reinterpret_cast
принудительно преобразует void*
в целевой тип данных. Это не гарантирует никакой безопасности, и ваша программа может выйти из строя, поскольку базовый объект может быть чем угодно.
Например, вы можете преобразовать myclass*
в void*
, а затем использовать reinterpret_cast
, чтобы преобразовать его в yourclass*
, который может иметь совершенно другой макет.
Так что лучше и рекомендуется использовать static_cast