Zaawansowane CPP/Ćwiczenia 6: Funkcje typów i inne sztuczki

From Studia Informatyczne

Ćwiczenie 1

Napisz szablon, który sprawdza czy jego parametr posiada typ stowarzyszony value_type, np.:

 has_value_type<std::vector<int> >::yes

powinno mieć wartość true, a

 has_value_type<int>::yes

wartość false.

Podpowiedź

Można wzorować się na szablonie is_class.

Rozwiązanie

Zasada jest taka sama, jak w przypadku sprawdzania czy dany typ jest klasą. Trzeba tylko wybrać odpowiedni argument pierwszej funkcji testującej. Odpowiednie wyrażenie to U::value_type, które nie jest poprawne dla typów U nie posiadających stowarzyszonego typu value_type. Definiujemy więc:

template<typename U> static one test(typename U::value_type );

Reszta jest jak dla Is_class. Całość kodu jest zamieszczona w pliku has_value_type.cpp

Ćwiczenie 2

Napisz szablon, który sprawdza czy jeden z jego argumentów dziedziczy z drugiego.

Podpowiedź

Wskaźnik Derived* może być konwertowany do Base* tylko jeśli Derived dziedziczy z Base lub Base jest typem void. Można więc wykorzystać szablon Convertible.

Rozwiązanie

Zgodnie ze wskazówką sprawdzamy, korzystając z klasy Is_convertible, czy Derived* może być skonwertowany do Base* i czy Base nie równa się void:

template<typename Derived, typename Base > struct Is_derived {
    enum {yes = Is_convertible<Derived*,Base*>::yes &&
         !Is_convertible<Base*,void*>::same_type
    };
};

Proszę zwrócić uwagę, że porównywane są typy Base* i void*. Porównanie typów Base i void za pomocą szablonu Convertible nie jest możliwe, bo prowadzi do nielegalnego wyrażenia test(void). Całość kodu jest zawarta w pliku derived.cpp.

Ćwiczenie 3

Napisz szablon, który sprawdza czy dany typ jest wymieniony w podanej liście typów. Np.:

 in<int,TypeList<int,TypeList<double,NullType> >>::yes

jest prawdziwe, a

 in<int,TypeList<char,TypeList<double,NullType> >>::yes

jest fałszywe.

Rozwiązanie

Idea rozwiązania jest następująca: jeśli nasz podany typ jest głową listy, to wiemy, że należy do listy:

template<typename T, typename Tail> struct In<T,TypeList<T,Tail> >{
    enum {yes=1};
};

Jeśli nie, to sprawdzamy czy należy do ogona:

template<typename T, typename TL> struct In {
    enum {yes=In<T, typename TL::Tail>::yes };
};

Jeśli dojdziemy do końca listy, czyli do Null_type, oznacza to, że szukanego typu nie ma na liście.

template<typename T> struct In<T,Null_type> {
    enum {yes=0};
};

Kod zawarty jest w pliku typelist.h.

Ćwiczenie 4

Implementacja indeksowania listy typów podana na wykładzie w przypadku przekroczenia zakresu powoduje błąd kompilacji. Napisz mniej restrykcyjną wersję tego szablonu, która w takiej sytacji "zwraca" typ:

 struct Empty_type {};

Rozwiązanie

Rozwiązanie jest podobne jak dla szablonu At:

template<int N,typename T> struct At2 {
    typedef typename At2<N-1, typename T::Tail>::Result Result;
};

template<typename H,typename T> struct At2<1,TypeList<H,T> > {
    typedef H Result;
};

Dodajemy tylko specjalizację, która w przypadku napotkania końca listy zwraca Empty_type:

template<int N> struct At2<N,Null_type> {
    typedef Empty_type Result;
};

Kod zawarty jest w pliku typelist.h.