/* * Autor: Piotr Buczkowski, 192902, N1-ISIII2 * * Temat: boost::type_traits * * Opis dotyczy biblioteki w wersji 1.38 * * Biblioteka "boost::type_traits" zawiera zbiór specyficznych klas cech, * z których każda obejmuje pojedynczą cechę systemu C++. * Na przykład czy dany typ jest wskaźnikiem czy referencją? * Lub czy typ posiada konstruktor domyślny albo kwalifikator "const"? * * Klas "type_traits" używamy ich w programowaniu generycznym * do określenia właściwości danego typu i optymalizacji * odpowiednich dla danego przypadku. * * Biblioteka "type_traits" zawiera także zbiór klas * dokonujących specyficznych transformacji na danym typie. * Potrafią na przykład usunąć kwalifikator "const" lub "volatile". * Każda klasa dokonująca transformacji definiuje * wewnętrzny składnik "type" będący typem transformacji. * */ ---- /* * Szablon "integral_constant" jest w hierarchii dziedziczenia * przodkiem klas "true_type" oraz "false_type" */ template struct integral_constant { typedef integral_constant type; typedef T value_type; static const T value = val; }; /* * Klasy "type_traits" mają wspólną cechę: * każda dziedziczy po "true_type" jeśli ma określoną właściwość * lub po "false_type" w przeciwnym wypadku. * Tak więc: * "true_type" jest typem oznaczającym, że klasa posiada daną cechę * "false_type" jest typem oznaczającym, że klasa danej cechy nie posiada */ typedef integral_constant true_type; typedef integral_constant false_type; /* * Rozważmy najprostszą klasę w bibliotece: "is_void". * Dziedziczy ona po "true_type" tylko jeśli jako typ "T" podamy "void". * Mówimy, że klasa szablonowa "is_void" dostarcza pełnej specjalizacji * (full-specialization), gdy typ "T" to "void". Jest to dość ważna technika, * ale czasem potrzebujemy czegoś pomiędzy rozwiązaniem generycznym * a pełną specjalizacją. To dlatego komitet standaryzacyjny zdefiniował * częściową specjalizację (partial template-class specialization). */ template struct is_void : public false_type{}; template <> struct is_void : public true_type{}; /* * Kolejny przykład to klasa "is_pointer". * Głowna wersja obsługuje przypadki gdy "T" nie jest wskaźnikiem, * a częściowa specjalizacja w przeciwnym razie. */ template struct is_pointer : public false_type{}; template struct is_pointer : public true_type{}; /* * Generalnie obowiązuje zasada, że aby napisać częściową specjalizację * należy uprzednio napisać główną wersję. */ /* * Przyjrzyjmy się przykładowi pokazującemu sposób w jaki klasy cech mogą być użyte. * Pokazuje on w jaki sposób klasy cech mogą być użyte * do podjęcia decyzji o optymalizacji w czasie kompilacji. * Rozważmy algorytm kopiujący z biblioteki standardowej: */ template Iter2 copy(Iter1 first, Iter1 last, Iter2 out); /* * W pewnych okolicznościach kopiowanie może być przeprowadzone najefektywniej * z użyciem funkcji "memcpy", ale by tak uczynić spełnione muszą być następujące warunki: * - operator przypisania nie został zdefiniowany przez programistę * - brak składników będących referencjami * - wszystkie klasy bazowe oraz składniki klasy muszą mieć trywialny operator przypisania * * Biblioteka "type_traits" dostarcza klasę "has_trivial_assignment" taką, * że "has_trivial_assignment::type" ma wartość "true" tylko wtedy, * gdy typ "T" ma trywialny operator przypisania. */ /* * Rozważmy następujący przykład: */ namespace detail { /* * Szablon funkcji robiącej wolną, ale bezpieczną kopię. */ template I2 do_copy(I1 first, I1 last, I2 out, const integral_constant&) { while(first != last) { *out = *first; ++out; ++first; } return out; } /* * W tej wersji funkcji kopiującej wymagamy, * aby oba iteratory wskazywały na ten sam typ * oraz by ostatni parametr był typu "true_type". */ template T* do_copy(const T* first, const T* last, T* out, const true_type&) { memcpy(out, first, (last-first)*sizeof(T)); return out+(last-first); } } /* * W tej wersji funkcji kopiującej posługujemy się funkcją "do_copy" * przekazując jej informację o typie wartości wskazywanych * przy użyciu "has_trivial_assign". * Spowoduje to wybranie wersji funkcji "do_copy" z "memcpy" * tylko tam gdzie tego chcemy * (czyli jedynie dla typów posiadających trywialny operator przypisania) * lub wersji wolniejszej lecz bezpiecznej w przeciwnym przypadku. */ template inline I2 copy(I1 first, I1 last, I2 out) { /* * Kopiujemy przy użyciu "memcpy" jeżeli "T" ma trywialny operator przypisania * oraz jeśli argumentami są wskaźniki. */ typedef typename std::iterator_traits::value_type value_type; return detail::copy_imp(first, last, out, boost::has_trivial_assign()); } /* * W następnym przykładzie rozważmy prostą parę dwóch typów: */ template struct pair { T1 first; T2 second; /* * Ta prosta para nie może przechowywać referencji, * gdyż konstruktor musiałby przyjmować wtedy referencje do referencji. */ pair(const T1& nfirst, const T2& nsecond) : first(nfirst), second(nsecond) {} }; /* * Aby zaradzić powyższemu problemowi posłużymy się klasą "add_reference", * która dodaje referencję do typu nie-referencyjnego. */ #include template struct improved_pair { T1 first; T2 second; improved_pair(boost::add_reference::type nfirst, boost::add_reference::type nsecond) : first(nfirst), second(nsecond) {} }; /* * Powyższe przykłady demonstrowały ideę klas cech * oraz to w jaki sposób mogą one zostać użyte. * Kompletnej listy dostępnych klas należy szukać w dokumentacji "boost". */