003
20.10.2003, 00:30 Uhr
virtual
Sexiest Bit alive (Operator)
|
Stichwort sind hier die Templates iterator und iterator_traits. Ein nicht trivialer Iterator (also etwas, was kein Pointer ist), sollte vom Template iterator abgeleitet werden. Dieses Template ist recht simple, es definiert 5 Iterator specifische Typen:
C++: |
template <class C, class T, class D = ptrdiff_t, class P = T*, class R = T&> struct iterator { typedef C iterator_category; // Typ des Iterators: forward, bidir, random ... typedef T value_type; // Der Typ vom Element, das dem Iterator zugrunde liegt typedef D difference_type; // Typ, um Abstände zu messen typedef P pointer; // Zeiger auf value_type (in der Regel) typedef R reference; // Reference auf value_type (in der Regel) }
|
Ziel dieser Klasse ist es, für alle Iteratoren die gleichen Typ namen zu definieren. Egal, ob Du nun ein std::set<std::string> oder ein std::list<std::string> nimmst: in beiden Fällen gibt es einen Iterator (der meist nicht trivial ist) und dieser Iterator hat einen Typen value_type, der ein std::string ist, vereinfacht ausgedrückt:
C++: |
std::string == typename std::list<std::string>::iterator::value_type std::string == typename std::set<std::string>::iterator::value_type
|
Übertragen auf Deine Routine könnte man also in erster (!) Näherung schreiben:
C++: |
template<typename iter> void swap(iter elem_one, iter elem_two) { [b]typename iter::value_type[/b] temp = *elem_one; *elem_one = *elem_two; *elem_two = temp; };
|
Würde sogar manchmal Funktionieren; nämlich genau dann, wenn Du einen Iterator hast, der von og Template geerbt hat. Nun gilt das Iteratorkonzept aber nicht nur für die Klassen, die von iterator abgelitten sind, sondern auch für so triviale Dinge wie einen Pointer. Nur dummerweise würde die Lösung, die ich grade für das swap nannte, genau hier versagen: ein Pointer hat nunmal keinen value_type. Wie viele andere Probleme in der Informatik auch, wird das Problem durch eine Indirektion gelöst: Wir definieren kurzerhand ein Template, welches die Characteristika eines Iterators beschreibt, die iterator_traits:
C++: |
template <I> struct iterator { typedef typename I::iterator_category iterator_category; typedef typename I::value_type value_type; typedef typename I::difference_type difference_type; typedef typename I::pointer pointer; typedef typename I::reference reference; };
|
Als Template parameter - so suggeriert diese Definition - wird immer ein iterator erwartet, und wir definieren exakt die gleichen Typen nochmal, diesmal eben in iterator_traits. Der wesentliche Unterschied ist einfach, daß in diesem Template als Parameter ein Iterator übergeben wird, und nicht, wie beim iterator Template selbst, fünf typen. Um nun auch mit Zeigern klarzukommen, wenden wir die partielle Templatespezialisierung an:
C++: |
template<class T> iterator_traits<T*> { typedef std::rand_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T&reference; };
|
Nun haben wir also nicht nur für von iterator abgelittene Klassen iterator_traits, sondern auch für Pointer (und damit eigentlich für alle Iteratoren. folgerichtig verwendet man die iterator_traits dann auch in seiner Funktion:
C++: |
template<typename iter> void swap(iter elem_one, iter elem_two) { [b]typename std::iterator_traits<iter>::value_type[/b] temp = *elem_one; *elem_one = *elem_two; *elem_two = temp; };
|
Ich denke, so sollte es gehen. -- Gruß, virtual Quote of the Month Ich eß' nur was ein Gesicht hat (Creme 21) Dieser Post wurde am 20.10.2003 um 00:32 Uhr von virtual editiert. |