Devo usar static_cast ou reinterpret_cast ao lançar um void * para qualquer

202

Static_cast e reinterpret_cast parecem funcionar bem para converter void * para outro tipo de ponteiro. Existe uma boa razão para favorecer um sobre o outro?

Andy
fonte
78
@anon Aparentemente, você nunca trabalhou com threads POSIX antes disso.
User470379
7
@ user470379 Uau ... essa é a razão pela qual eu cheguei nessa questão na SO! Excelente observação :-).
Ogre Psalm33

Respostas:

148

Usostatic_cast : é o elenco mais restrito que descreve exatamente qual conversão é feita aqui.

Há um equívoco de que o uso reinterpret_castseria uma correspondência melhor porque significa "ignorar completamente a segurança de tipo e apenas transmitir de A para B".

No entanto, isso realmente não descreve o efeito de a reinterpret_cast. Em vez disso, reinterpret_castpossui vários significados, e todos afirmam que "o mapeamento realizado por reinterpret_casté definido pela implementação". [5.2.10.3]

Mas, no caso específico de transmissão de void*paraT* o mapeamento, é completamente bem definido pelo padrão; ou seja, atribuir um tipo a um ponteiro sem tipo, sem alterar seu endereço.

Esta é uma razão para preferir static_cast.

Além disso, e sem dúvida mais importante, é o fato de que todo uso de reinterpret_casté absolutamente perigoso, porque converte qualquer coisa para qualquer outra coisa realmente (para ponteiros), enquanto static_casté muito mais restritivo, proporcionando um melhor nível de proteção. Isso já me salvou de bugs, onde acidentalmente tentei coagir um tipo de ponteiro para outro.

Konrad Rudolph
fonte
8

Esta é uma pergunta difícil. Por um lado, o Konrad faz um excelente argumento sobre a definição de especificação para reinterpret_cast , embora na prática provavelmente faça o mesmo. Por outro lado, se você estiver transmitindo entre tipos de ponteiro (como é bastante comum ao indexar na memória por um caractere *, por exemplo), static_cast gerará um erro do compilador e você será forçado a usar o reinterpret_cast de qualquer maneira.

Na prática, uso reinterpret_cast porque é mais descritivo da intenção da operação de conversão. Você certamente poderia argumentar que um operador diferente designasse apenas reinterpretações de ponteiros (que garantiam o mesmo endereço retornado), mas não há um no padrão.

usuario
fonte
6
" operador diferente para designar ponteiro reinterpreta apenas (que garantiu o mesmo endereço retornado) " Abraço? Esse operador é reinterpret_cast !
precisa
2
@curiousguy Não é verdade de acordo com o padrão. reinterpret_cast NÃO garante que o mesmo endereço seja usado. Somente que, se você reinterpretar_cast de um tipo para outro e depois voltar novamente , receberá o mesmo endereço com o qual começou.
ClydeTheGhost # 1/19
0

Sugiro usar sempre o elenco mais fraco possível.

reinterpret_cast pode ser usado para converter um ponteiro em um float . Quanto mais estrutura o elenco for, mais atenção será necessária.

No caso de char*, eu usaria elenco de estilo c, até que tenhamos alguns reinterpret_pointer_cast, porque é mais fraco e nada mais é suficiente.

Pavel Radzivilovsky
fonte
2
" reinterpret_cast is pode ser usado para converter um ponteiro em um flutuador. " Certamente não!
precisa
3
Provavelmentefloat f = *reinterpret_cast<const float*>(&p);
Ben Voigt
2
@BenVoigt Isso é transmitido entre ponteiros; um deles era um ponteiro de flutuação.
nodakai
5
@BenVoigt a "expressão inteira" não é um elenco embora. A expressão consiste em uma desreferência aplicada a um elenco. Você alegou que era possível lançar um ponteiro para float, o que é falso. Os moldes de expressão void **para const float *, em seguida, usa uma operação dereference (que não é um elenco), para converter const float *a float.
MM
2
@BenVoigt, você ofereceu esse código em resposta a alguém perguntando "Como faço para transmitir ..." e, quando alguém disse que o código é transmitido entre ponteiros (o que faz), você disse "Não"
MM
-7

Minha preferência pessoal é baseada na alfabetização de código como esta:

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

ou

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

Os dois fazem o mesmo no final, mas static_cast parece mais apropriado em um ambiente de aplicativo de middleware, enquanto a reinterpretação do elenco parece mais algo que você veria em uma biblioteca de nível inferior IMHO.

Robert Gould
fonte