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

202

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

Энди
источник
78
@anon Видимо, вы никогда раньше не работали с потоками POSIX.
user470379
7
@ user470379 Ого ... вот почему я попал на этот вопрос в SO! Отличное наблюдение :-).
Огр псалом 33

Ответы:

148

Использованиеstatic_cast : это самый узкий состав, который точно описывает, какое преобразование сделано здесь.

Существует заблуждение, что использование reinterpret_castбудет лучшим совпадением, потому что это означает «полностью игнорировать безопасность типов и просто приводить от А к В».

Тем не менее, это на самом деле не описывает эффект reinterpret_cast. Скорее, reinterpret_castимеет ряд значений, для всех из которых утверждается, что «выполняемое отображение определяется reinterpret_castреализацией». [5.2.10.3]

Но в частном случае приведение void*к T*отображению полностью определено стандартом; а именно, назначить тип указателю без указания типа без изменения его адреса.

Это причина для предпочтения static_cast.

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

Конрад Рудольф
источник
8

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

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

Ник
источник
6
msgstr " другой оператор для обозначения только переинтерпретации указателя (который гарантировал возвращение того же адреса) " Hug? Этот оператор есть reinterpret_cast !
любопытный парень
2
@curiousguy Не соответствует стандартам. reinterpret_cast НЕ гарантирует, что используется один и тот же адрес. Только если вы переинтерпретируете от одного типа к другому, а затем снова возвращаетесь , вы получите тот же адрес, с которого вы начали.
ClydeTheGhost
0

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

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

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

Павел Радзивиловский
источник
2
« reinterpret_cast is может использоваться для приведения указателя к плавающей точке. » Конечно, нет!
любопытный парень
3
Вероятноfloat f = *reinterpret_cast<const float*>(&p);
Бен Фойгт
2
@BenVoigt Это приведение между указателями; один из них оказался поплавковым указателем.
nodakai
5
@BenVoigt «все выражение» не является актером. Выражение состоит из разыменования, примененного к приведению. Вы утверждали, что можно было навести указатель наfloat , что неверно. Выражение приводится void **к const float *, а затем использует операцию разыменования (которая НЕ является приведением) для преобразования const float *в float.
ММ
2
@BenVoigt вы предложили этот код в ответ на вопрос «Как я могу разыграть ...», а затем, когда кто-то сказал, что код преобразуется между указателями (что он делает), вы ответили: «Нет»
ММ,
-7

Мои личные предпочтения основаны на грамотности кода:

void* data = something;
MyClass* foo = reinterpret_cast<MyClass*>(data);
foo->bar();

или

typedef void* hMyClass; //typedef as a handle or reference
hMyClass = something;
const MyClass& foo = static_cast<MyClass&>(*hMyClass);
foo.bar();

Они оба делают то же самое в конце, но static_cast кажется более подходящим в промежуточном ПО, среде приложения, в то время как переосмысление приведено больше как то, что вы видели бы в низкоуровневой библиотеке IMHO.

Роберт Гулд
источник