Własny operator rzutowania

2008-01-30 22:27

W C++ nadal można używać “tradycyjnego” rzutowania w postaci (typ)wyrażenie, bo cały czas zachowywana jest przynajmniej częściowa kompatybilność wstecz ze starym C. Jak wiadomo jednak, zaleca się raczej stosowanie nowych (tzn. wprowadzonych w C++; faktycznie są już one raczej stare) operatorów rzutowania, czyli static_cast, dynamic_cast, reinterpet_cast i const_cast. Jest tak między innymi dlatego, iż pozwalają one rozróżnić rzutowania bezpieczne – które można sprawdzić w czasie kompilacji i które zawsze “mają sens” – od pozostałych.
Składnia takiego rzutowania to oczywiście something_cast<typ>(wyrażenie). Jeśli przyjrzymy się jej bliżej, to zauważymy interesującą rzecz. Otóż jest to bardzo podobne do pewnej formy wywołania szablonu funkcji. Ten fakt plus pewne zasady dotyczące dedukcji argumentów takich szablonów sprawiają, że zasadniczo możemy w C++ tworzyć… własne operatory rzutowania.

Prawdopodobnie najbardziej znanym jest implicit_cast, czyli operator rzutowania niejawnego. Możemy go zdefiniować następująco:

  1. template <typename Dest, typename Src>
  2. inline Dest implicit_cast(const Src& arg)
  3. {
  4.    return arg;
  5. }

i wywoływać w bardzo porządny sposób:

  1. string str = implicit_cast<string>("Hello world");

Ze strony swojego dzialania operator ten nie jest specjalnie ciekawy, bo to przykład językowego purytanizmu czystej wody. implicit_cast wykonuje bowiem te konwersje, które kompilator i tak by przeprowadził niejawnie; widać to zresztą po kodzie odpowiadającej mu funkcji.
No właśnie – warto zauważyć, że jest w gruncie rzeczy zwykła funkcja szablonowa. Działa ona jak operator rzutowania, gdyż drugi z argumentów tego szablonu – typ źródłowy – może zostać wydedukowany z argumentu funkcji (czyli wyrażenia, które rzutujemy). Musimy jedynie podać pierwszy z nich, czyli typ docelowy – ale tego można się było spodziewać. Trzeba zaznaczyć, że kolejność obu parametrów szablonu musi być właśnie taka, ponieważ ich przestawienie spowodowałoby, że automatyczna dedukcja nie mogła by zostać przeprowadzona. Zachodzi ona bowiem począwszy od końcowych parametrów.

Prawdopodobnie taki własny operator rzutowania to przede wszystkim efektowna sztuczka. Nic więc dziwnego, że kilka przykładów (np. lexical_cast) jest częścią biblioteki Boost, która niemal w całości jest zbiorem takich językowych gadżetów :) Sam aczkolwiek wymyśliłem dosyć dawno temu operator “rzutowania bezwzględnego”, który wygląda mniej więcej tak:

  1. template <typename Dest, typename Src>
  2. inline Dest absolute_cast(const Src& arg)
  3. {
  4.    return *(reinterpret_cast<Dest*>(const_cast<Src*>(&arg)));
  5. }

Służy on do dosłownego rzutowania dowolnego typu na dowolny inny w sensie zmiany interpretacji bajtów. Zdaje się on działać dla większości typów w C++ (głównym wyjątkiem są typy referencyjne, co nie jest specjalną niespodzianką) i bywa całkiem przydatny.

Naturalnie można wymyślić jeszcze sporo innych niestandardowych operatorów rzutowania, jak choćby konwersje między napisami w różnych kodowaniach (ANSI, Unicode) czy do/z jakichś własnych typów, opakowujących klasy zewnętrzne. W każdym razie widać znowu (i pewnie nie ostatni raz), że C++ pozwala nam na bardzo dużo, skoro nawet te nieco rozwlekłe *_casty można jakoś oswoić do własnych potrzeb :]

Tags: , ,
Author: Xion, posted under Programming »



Adding comments is disabled.

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.