2012-11-11 4 views
10

Przechodziłem przez jakiś kod w CodeProject i natrafiłem na następujący kod do odlewania w C++.Implementacja języka C++ Cast

template <class OutputClass, class InputClass> 
union horrible_union{ 
    OutputClass out; 
    InputClass in; 
}; 
template <class OutputClass, class InputClass> 
inline OutputClass horrible_cast(const InputClass input){ 
    horrible_union<OutputClass, InputClass> u; 
    u.in = input; 
    return u.out; 
} 

Dlaczego obsada została zaimplementowana w powyższy sposób. Dlaczego nie możemy po prostu wykonać rzutowania ręcznego? Czy ktoś może podać przykład, kiedy normalny rzut nie zadziała?

+4

Uzyskanie dostępu do członka związku po ustawieniu innego członka jest niezdefiniowanym zachowaniem, więc wszystko jest lepsze niż to podejście "koszmarnego". –

+0

Prawdopodobnie dlatego nazwano ją okropną obsadą. Ma to na celu zaimplementowanie zachowania specyficznego dla kompilatora. – KodeWarrior

Odpowiedz

9

Podejście to pozwala odejść z każdą obsadą, choć opiera się na niezdefiniowanym zachowaniu.

Normalna obsada będzie narzekać podczas rzucania między niezwiązanymi typami, podczas gdy nie będzie.

struct A{}; 
struct B{}; 

template <class OutputClass, class InputClass> 
union horrible_union{ 
    OutputClass out; 
    InputClass in; 
}; 
template <class OutputClass, class InputClass> 
inline OutputClass horrible_cast(const InputClass input){ 
    horrible_union<OutputClass, InputClass> u; 
    u.in = input; 
    return u.out; 
} 

int main() 
{ 
    A a; 
    B b; 
    a = horrible_cast<A,B>(b); //this compiles 
    a = reinterpret_cast<A>(b); //this doesn't 
} 

Podsumowanie: to jest okropne, nie rób tego.

+1

Jeśli pracujesz nad określoną architekturą i przy użyciu określonego kompilatora (w przypadku niektórych systemów wbudowanych), może to być prawidłowe podejście do konwersji typów, które mają sens wyłącznie w tej architekturze. Ale nadal jest niebezpiecznie, zgadzam się. – bitmask

2

Używanie związku w ten sposób jest ogólnie mniej więcej równoznaczne z twardym reinterpret_cast wskaźników. Jednak to nie kopiuje obiektów, twój przykład robi (dwukrotnie nawet z RVO, bardziej efektywnym byłoby mieć argument const InputClass& input). Możesz więc bezpośrednio operować na jego wynikach bez mutowania oryginalnego obiektu.

Co dokładnie jest przydatne dla ... hm. Nie sądzę, że jest jakikolwiek dobry przypadek użycia, należy zawsze unikać niezajmowanych rzutów, a jak zauważył David Hammen, ten jest całkowicie nieokreślony (choć normalnie będzie działał, jeśli zostanie użyty "poprawnie").

+0

Może ich sklep ma politykę przeciwko 'reinterpret_cast', ale nie jest to luka. – Barmar

+0

W takim przypadku byłoby jeszcze gorzej: "reinterpret_cast" jest niebezpieczne, ale ten jest gorszy. – leftaroundabout

+0

Osobiście wolałbym widzieć "reinterpret_cast" (i być może musiałem przejść po zrzeczeniu się, jeśli jest jakaś reguła przeciwko temu), niż ukrywać obsadę w ten sposób. –