Zasadniczo w C++ zmienne mają jednoznacznie określone typy, znane w czasie kompilacji. Istnieją oczywiście pewne mechanizmy, które zdają się tę zasadę lekko naciągać (dziedziczenie i polimorfizm, szablony), ale bez znaczącej nieścisłości można przyjąć, że jest ona prawdziwa. W języku kompilowanym nie jest to zresztą nic nadzwyczajnego.
Z kolei w językach skryptowych i interpretowanych dość powszechne jest używanie zmiennych bez określonego (tj. jawnie podanego w deklaracji) typu. To, co obecnie przechowuje liczbę, za chwilę może zawierać ciąg znaków czy odwołanie do obiektu i nie ma z tym specjalnego problemu. Typy w takich językach oczywiście istnieją, ale mają je wartości, nie zaś zmienne.
Czasami coś podobnego – czyli zmienna mogąca przechowywać wartości różnego rodzaju – przydaje się i w C++. Wtedy niektórzy przypominają sobie o void*
, ale “ogólny wskaźnik na coś” rzadko jest w tym przypadku szczytem marzeń. Jego podstawową wadą jest to, że wymaga on przechowania gdzieś poza nim informacji o docelowym typie wartości, na którą pokazuje – jeśli chcemy ją później wykorzystać, rzecz jasna. Równie poważną jest fakt, że mamy tu do czynienia z wskaźnikiem; pamięć, na którą on pokazuje, musi być kiedyś zwolniona.
Dlatego też lepszym rozwiązaniem jest biblioteka Any z Boosta, umożliwiająca korzystanie ze zmiennych “typu dowolnego”. Reprezentuje go klasa boost::any
, mogąca przechowywać wartości prawie dowolnego rodzaju (wymaganiem jest głównie to, by ich typy posiadały zwykły konstruktor kopiujący):
Ponieważ jednak jest to wciąż C++, a nie język skryptowy, do wartości zapisanych w obiekcie any
bezpośrednio dostać się nie da. W szczególności nie można liczyć na niejawne konwersje powyższych zmiennych do typów int
, string
czy Foo
. Zamiast tego korzysta się ze specjalnego operatora rzutowania any_cast
, który pozwala na potraktowanie wartości zapisanej w any
zgodnie z jej właściwym typem:
W przeciwieństwie do void*
, próba jej reinterpretacji na inny typ danych skończy się wyjątkiem. Nie trzeba jednak polegać na jego łapaniu, jeśli docelowego typu nie jesteśmy pewni: boost::any
pozwala też pobrać jego type_info
(tj. to, co zwraca operator typeid
) bez dokonywania rzutowania.
I to w sumie wszystko jeśli chodzi o tę klasę, a jednocześnie i całą bibliotekę. Mimo swej niewątpliwej prostoty jest ona bardzo przydatna tam, gdzie potrzebujemy zmiennych przechowujących różne rodzaje wartości. Warto więc się z nią zapoznać :)
A tak prawdę mówiąc, miałeś kiedyś potrzebę użycia zmiennej “bez typu”? :)
Interpreter języka skryptowego czy jakikolwiek system właściwości (properties) w obiektach, używany w dowolnym celu, to typowe przykłady, gdzie coś takiego się przydaje. Dla mnie aczkolwiek boost::any okazał się przydatny do nieco innego zastosowania; chwilowo nie napiszę, jakiego :)
Jak wyżej. Tyle że używałem bardziej wyspecjalizowanej struktury boost::variant :)
Ja jakoś nie widzę zastosowania. Ale dobrze wiedzieć, że coś takiego jest. Ma jak widzę prosty interfejs, co do Boosta niepodobne ;)
A czy to nie to samo co “auto” z C++0x?
Nie, auto służy by kompilator sam określił typ :) Tu chodzi o dynamiczne typowanie, a boost::any to proteza dorabiający ten feature to który ma nazwijmy, silną typizacje.
fixed :P
… ten feature do języka który ma….
Całkiem fajna rzecz, czasami mi się takie coś by właśnie przydało. Eniłej, jeśli chodzi o inne języki, dynamic z C# 4.0 jest bardzo blisko tego? ;->
[w C# 3.0 też można to osiągnąć, tylko tragicznie kod wygląda :D ]
Jest bardzo daleko, bo wyprzedza boost::any o parę kilometrów ;-) Z kolei odpowiednikiem any w C# jest po prostu zwykły typ object
.