/* * 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 <class T, T val> struct integral_constant {
typedef integral_constant<T, val> 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<bool, true> true_type; typedef integral_constant<bool, false> false_type;
/* * Rozważmy najprostszą klasę w bibliotece: „is_void<T>”. * 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 <typename T> struct is_void : public false_type{};
template <> struct is_void<void> : public true_type{};
/* * Kolejny przykład to klasa „is_pointer<T>”. * Głowna wersja obsługuje przypadki gdy „T” nie jest wskaźnikiem, * a częściowa specjalizacja w przeciwnym razie. */ template <typename T> struct is_pointer : public false_type{};
template <typename T> struct is_pointer<T*> : 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 <typename Iter1, typename Iter2> 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<T>::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<typename I1, typename I2, bool b> I2 do_copy(I1 first, I1 last, I2 out, const integral_constant<bool, b>&) { 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<typename T> 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<value_type>”. * 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<typename I1, typename I2> 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<I1>::value_type value_type; return detail::copy_imp(first, last, out, boost::has_trivial_assign<value_type>());
}
/* * W następnym przykładzie rozważmy prostą parę dwóch typów: */ template <typename T1, typename T2> 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 <boost/type_traits/add_reference.hpp> template <typename T1, typename T2> struct improved_pair {
T1 first; T2 second;
improved_pair(boost::add_reference<const T1>::type nfirst, boost::add_reference<const T2>::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”. */