Czy powinienem używać static_cast lub reinterpret_cast podczas rzucania void * na cokolwiek

202

Wydaje się, że zarówno static_cast, jak i reinterpret_cast działają dobrze w przypadku rzutowania void * na inny typ wskaźnika. Czy istnieje dobry powód, aby faworyzować jednego nad drugim?

Andy
źródło
78
@anon Najwyraźniej nigdy wcześniej nie pracowałeś z wątkami POSIX.
user470379,
7
@ user470379 Wow ... to jest właśnie powód, dla którego trafiłem na to pytanie w SO! Doskonała obserwacja :-).
Ogre Psalm33

Odpowiedzi:

148

Zastosowaniestatic_cast : jest to najwęższa obsada, która dokładnie opisuje dokonaną tutaj konwersję.

Istnieje błędne przekonanie, że użycie reinterpret_castbyłoby lepsze, ponieważ oznacza „całkowicie zignoruj ​​bezpieczeństwo typu i po prostu rzuć z A na B”.

Jednak tak naprawdę nie opisuje to efektu reinterpret_cast. Raczej reinterpret_castma wiele znaczeń, z których wszystkie utrzymują, że „wykonywane mapowanie reinterpret_castjest zdefiniowane w ramach implementacji”. [5.2.10.3]

Ale w szczególności przypadku odlewania z void*do T*mapowania jest zupełnie dobrze zdefiniowane przez standard; mianowicie, aby przypisać typ do wskaźnika bez typu, bez zmiany jego adresu.

To jest powód, aby preferować static_cast.

Ponadto, i prawdopodobnie ważniejsze, jest fakt, że każde użycie reinterpret_castjest wręcz niebezpieczne, ponieważ przekształca wszystko w cokolwiek innego (w przypadku wskaźników), podczas gdy static_castjest znacznie bardziej restrykcyjne, zapewniając w ten sposób lepszy poziom ochrony. To już uratowało mnie przed błędami, w których przypadkowo próbowałem zmusić jeden typ wskaźnika do drugiego.

Konrad Rudolph
źródło
8

To trudne pytanie. Z jednej strony Konrad doskonale rozumie definicję specyfikacji dla reinterpret_cast , chociaż w praktyce prawdopodobnie robi to samo. Z drugiej strony, jeśli używasz rzutowania między typami wskaźników (co jest dość powszechne, na przykład podczas indeksowania w pamięci za pomocą znaku char *), static_cast wygeneruje błąd kompilatora i będziesz zmuszony użyć reinterpret_cast .

W praktyce używam reinterpret_cast, ponieważ jest to bardziej opisowe zamiar operacji rzutowania. Z pewnością możesz uzasadnić, że inny operator wyznaczy tylko reinterpretację wskaźnika (co gwarantowało zwrot tego samego adresu), ale nie ma takiego w standardzie.

Nacięcie
źródło
6
inny operator, który wyznaczy tylko wskaźnik, interpretuje tylko (co gwarantowało zwrot tego samego adresu) ” Przytulić? Ten operator jest reinterpret_cast !
ciekawy,
2
@curiousguy Nieprawda zgodnie ze standardem. reinterpret_cast NIE gwarantuje, że użyty zostanie ten sam adres. Tyle, że jeśli ponownie interpretujeszcast z jednego typu na inny, a następnie z powrotem , otrzymasz ten sam adres, od którego zacząłeś.
ClydeTheGhost
0

Zawsze sugeruję używanie najsłabszej możliwej obsady.

reinterpret_castmoże być użyty do rzutowania wskaźnika na float. Im bardziej rzutuje się na strukturę, tym więcej uwagi wymaga jej użycie.

W przypadku char*używałbym rzutowania w stylu c, dopóki nie będziemy mieli reinterpret_pointer_cast, ponieważ jest on słabszy i nic więcej nie jest wystarczające.

Pavel Radzivilovsky
źródło
2
reinterpret_cast może być użyty do rzutowania wskaźnika na liczbę zmiennoprzecinkową. ” Na pewno nie!
ciekawy,
3
Prawdopodobniefloat f = *reinterpret_cast<const float*>(&p);
Ben Voigt,
2
@BenVoigt To rzucanie między wskaźnikami; jednym z nich był wskaźnik zmiennoprzecinkowy.
nodakai
5
@BenVoigt „całe wyrażenie” nie jest jednak obsadą. Wyrażenie składa się z dereferencji zastosowanej do obsady. Twierdziłeś, że można rzucić wskaźnik na float, co jest fałszem. Odlewy ekspresyjne void **do const float *, a następnie używa operację wyłuskiwania (co nie jest odlew), aby przekształcić const float *się float.
MM
2
@BenVoigt zaoferowałeś ten kod w odpowiedzi na pytanie, jak ktoś pyta „Jak rzutować ...”, a następnie, gdy ktoś powiedział, że kod rzuca między wskaźnikami (co robi), powiedziałeś „Nie”
MM
-7

Moje osobiste preferencje oparte są na umiejętności pisania kodu:

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

lub

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

Oba robią to samo na końcu, ale static_cast wydaje się bardziej odpowiednie w środowisku aplikacji, podczas gdy reinterpretacja obsady wydaje się bardziej jak coś, co można zobaczyć w bibliotece niższego poziomu IMHO.

Robert Gould
źródło