Должен ли я использовать static_cast или reinterpret_cast при приведении void * к чему угодно

И static_cast, и reinterpret_cast, похоже, отлично подходят для преобразования void* в другой тип указателя. Есть ли веская причина отдавать предпочтение одному другому?


person Andy    schedule 21.11.2008    source источник
comment
@anon Очевидно, вы никогда раньше не работали с потоками POSIX.   -  person user470379    schedule 23.12.2010
comment
@ user470379 Вау ... именно по этой причине я задал этот вопрос в SO! Замечательное наблюдение :-).   -  person Ogre Psalm33    schedule 21.06.2011


Ответы (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 гораздо более ограничительно, что обеспечивает лучший уровень защиты. Это уже избавило меня от ошибок, когда я случайно пытался преобразовать один тип указателя в другой.

person Konrad Rudolph    schedule 21.11.2008

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* или наоборот, что зависит от платформы, потому что конкретные представления floats и ints не обязательно будут иметь что-либо общее между собой.

Короче говоря, если вы когда-нибудь обнаружите, что выполняете преобразование, в котором приведение логически значимо, но не обязательно может быть успешным во время выполнения, избегайте 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 не гарантируется переносимостью.

person templatetypedef    schedule 16.02.2011
comment
Чтобы уточнить: то, что автор имеет в виду здесь под _1 _..., не обязательно гарантирует работу во время выполнения, ваша программа может вылететь позже. Если вы static_cast преобразовали базовый тип в производный тип, он будет работать во время выполнения (т. Е. Вы не получите исключение или указатель NULL), но результат может быть указывая на неправильную ячейку памяти, если задействовано множественное наследование. (Дополнительные сведения см. В этом ответе.) Только dynamic_cast выполнит проверку времени выполнения (с использованием RTTI) и корректно завершится с ошибкой, если приведение является недействительным. - person andrewtc; 10.08.2014

Это сложный вопрос. С одной стороны, Конрад отлично замечает определение спецификации для reinterpret_cast, хотя на практике он, вероятно, делает то же самое. С другой стороны, если вы выполняете приведение типов указателей (что довольно часто встречается при индексировании в памяти с помощью символа *, например), static_cast сгенерирует ошибку компилятора, и вы будете вынуждены в любом случае использовать reinterpret_cast.

На практике я использую reinterpret_cast, потому что он лучше описывает цель операции приведения. Вы, конечно, могли бы привести аргумент в пользу того, чтобы другой оператор назначал только переинтерпретацию указателя (что гарантировало возвращение того же адреса), но в стандарте такого нет.

person Nick    schedule 21.11.2008
comment
другой оператор для обозначения только переинтерпретации указателя (который гарантировал возврат того же адреса) Hug? Этот оператор - reinterpret_cast! - person curiousguy; 19.12.2011
comment
@curiousguy Не соответствует стандарту. reinterpret_cast НЕ гарантирует, что используется тот же адрес. Только если вы переинтерпретируете_cast от одного типа к другому , а затем снова, вы вернетесь к тому же адресу, с которого начали. - person ClydeTheGhost; 01.03.2019

Вероятно, вы получили это void* с неявным преобразованием, поэтому вам следует использовать static_cast, потому что он ближе всего к неявному преобразованию.

person sharptooth    schedule 16.02.2011

Используйте для этого static_cast. Только в самых редких случаях, когда другого выхода нет, используйте reinterpret_cast.

person Asha    schedule 16.02.2011

Трансляция в void* и обратно с использованием static_cast и reinterpret_cast идентична. См. Ответ на ссылке. Но обычно static_cast предпочтительнее, потому что это более узкое и в целом (но не в этом конкретном случае) более безопасное преобразование.

person anton_rh    schedule 26.06.2021

Я предлагаю всегда использовать самый слабый слепок.

reinterpret_cast может использоваться для приведения указателя к float. Чем больше повязка разрушает структуру, тем большего внимания требует ее использование.

В случае char*, я бы использовал приведение в стиле c, пока у нас не будет reinterpret_pointer_cast, потому что он слабее и ничего другого недостаточно.

person Pavel Radzivilovsky    schedule 14.12.2009
comment
reinterpret_cast может использоваться для приведения указателя к float. Конечно, нет! - person curiousguy; 19.12.2011
comment
конечно да, любопытный парень. Проверьте стандарт еще раз. - person Pavel Radzivilovsky; 19.12.2011
comment
Проверьте стандарт еще раз. Проверяли ли вы вы стандарт? - person curiousguy; 21.12.2011
comment
да. Не уверен, что вы имеете в виду под примером - единственное предполагаемое использование для этого - пересылка числа с плавающей точкой через cookie, ожидающий указателя. - person Pavel Radzivilovsky; 21.12.2011
comment
Как указать указатель на число с плавающей запятой? 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
comment
Наверное float f = *reinterpret_cast<const float*>(&p); - person Ben Voigt; 07.08.2013
comment
@BenVoigt Это преобразование между указателями; один из них оказался указателем с плавающей запятой. - person nodakai; 01.06.2016
comment
@nodakai: Нет, он интерпретирует биты p как float. Для этого используется указатель на p, но p принудительно устанавливается на float, а не на float*. - person Ben Voigt; 01.06.2016
comment
@BenVoigt: Этот reinterpret_cast преобразуется из некоторого указателя (что бы ни было p) в const float*. Затем этот указатель разыменовывается, чтобы получить значение с плавающей запятой. Это действительно приводит к тому, что битовое представление p используется как float, но сам reinterpret_cast по-прежнему приводит тип указателя к другому типу указателя. - person Cemafor; 16.06.2016
comment
@cemafor no не от того, что p равно float*. От указателя к тому, что такое p - двойной указатель. - person Ben Voigt; 16.06.2016
comment
@BenVoigt: Извините, я имел в виду, что тип, приводимый reinterpret_cast, является указателем на любой тип p. Если p - это double, это будет преобразование от double* к const float*. Тип &p - double*. - person Cemafor; 16.06.2016
comment
@BenVoigt * перед reinterpret_cast не является частью актерского состава. Это оператор почтения, ответивший на результат приведения. В этом коде reinterpret_cast преобразует указатель в указатель. Затем последний указатель разыменовывается после завершения приведения. - person M.M; 26.07.2018
comment
@ M.M: Я знаю, что * - это отдельный оператор, но мой комментарий по-прежнему верен. Вы пропустили дополнительный & в операнде reinterpret_cast? - person Ben Voigt; 26.07.2018
comment
@BenVoigt Я не проглядел, и ваш комментарий неверен. reinterpret_cast<const float *>(&p) преобразует указатель в указатель. Содержимое < > - это тип результата приведения. - person M.M; 27.07.2018
comment
Затем к результату приведения применяется унарный оператор разыменования. Оператор * преобразует const float * в float (оператор приведения не преобразуется в float) - person M.M; 27.07.2018
comment
@ M.M: Все выражение, взятое вместе, *reinterpret_cast<const float*>(&p), действительно приводит p к float. Мой комментарий не ошибочен. - person Ben Voigt; 27.07.2018
comment
@BenVoigt, однако, все выражение не является приведением. Выражение состоит из разыменования, примененного к приведению. Вы утверждали, что можно было привести указатель на float, что неверно. Выражение преобразует void ** в const float *, а затем использует операцию разыменования (которая НЕ является преобразованием) для преобразования const float * в float. - person M.M; 27.07.2018
comment
@ M.M: На самом деле это не актерский состав. Я никогда не называл это одним. - person Ben Voigt; 27.07.2018
comment
@BenVoigt, вы предложили этот код в ответ на вопрос о том, как мне выполнить приведение ..., а затем, когда кто-то сказал, что код отбрасывается между указателями (что и происходит), вы сказали, что нет - person M.M; 27.07.2018
comment
@ M.M: Я предположил, что человек, спрашивающий, возможно, сказал актерский состав, но хотел, чтобы конверсия не была прямым исполнением. А затем другой пользователь заявил, что он преобразовал void* в float*, что было неверно. Он приводил void** к float*, чтобы преобразовать void* в float. - person Ben Voigt; 27.07.2018

reinterpret_cast принудительно преобразует void* в целевой тип данных. Это не гарантирует никакой безопасности, и ваша программа может выйти из строя, поскольку базовый объект может быть чем угодно.

Например, вы можете преобразовать myclass* в void*, а затем использовать reinterpret_cast, чтобы преобразовать его в yourclass*, который может иметь совершенно другой макет.

Так что лучше и рекомендуется использовать static_cast

person mukeshkumar    schedule 16.02.2011
comment
static_cast не предотвратит этого. После того, как указатель превратился в void *, вы можете static_cast преобразовать его в любой тип указателя. - person Dan O; 16.02.2011