![]() |
Generics <> Templates ?
In letzter Zeit stößt man ja öfter auf den Begriff Generics, weshalb ich mich mal versucht habe schlau zu machen. Aber irgendwie wird mir der Unterschied zwischen Generics und Templates (c++-Templates) nicht wirklich klar. Für mich hört sich das an wie zwei Begriffe für die ein und die selbe Sache
Man baut sich eine abstrakte Vorlage, die später (z.B. durch einen Prä-Prozessor) typisiert wird. Vielleicht kann mir hier mal jemand auf die Sprünge helfen :) |
Re: Generics <> Templates ?
Zitat:
Vorteil ist, dass du einen Generic direkt kompilieren kannst und somit schon ohne Spezialiserung Fehlermeldungen bekommst. Nachteil ist, dass du keine Operatoren benutzen kannst. Und auch sonst kannst du nur die Dinge benutzen, die du durch Einschränkungen der Typenparameter festgelegt hast. Hier mal ein Bleistift (ist allerdings in Chrome, da Delphi noch keine Generics hat):
Delphi-Quellcode:
Wie du siehst, kann ich die Methode benutzen, weil ich T so eingeschränkt habe, dass es IDoSomething implementieren muss.
type
IDoSomething = public interface method DoSomething : String; end; GenericSample<T> = public static class where T is IDoSomething; public class method Test(instance : T); end; implementation class method GenericSample<T>.Test(instance : T); begin var miep := instance.DoSomething(); Console.WriteLine(miep); end; |
Re: Generics <> Templates ?
Templates sind statisch polymorph, sämtlicher typenspezifischer Code wird also zur Übersetzungszeit generiert. Das führt z.B. bei extensiver Verwendung von Template-Klassen wie der STL zu einem nicht unbeträchtlichen Code-Bloat, da sämtlicher Code für jede Typkombination, mit der das Template instantiiert wird, neu generiert wird.
Da der Code zur Compilierzeit an die Typen angepaßt werden muß, muß er dem Compiler auch zur Verfügung stehen; daher ist es nötig, die gesamte Implementation eines Templates in Headerdateien unterzubringen. (Es gibt dafür zwar das Schlüsselwort export, doch implementiert das praktisch kein Compiler.) Dies ist bei Generics nicht der Fall, da sie zur Laufzeit typisiert werden. Generics sind allerdings derzeit dadurch im Vorteil, daß man die benötigte Schnittstelle angeben und dadurch undurchschaubaren Fehlermeldungen vorbeugen kann; das wird aber in C++0x mit Concepts auch möglich sein. Und natürlich gibt es für Generics keine Wertparameter. Damit fällt ein Großteil der Möglichkeiten der Template-Metaprogrammierung weg, durch die in C++ ganz verrückte Dinge möglich werden, z.B. die Wahl eines Typs abhängig davon, ob zwei andere Typen gleich sind:
Code:
typedef IfThenElseType <TypesAreEqual <T, const T>::result,
RandomIterator <const T, Base, direction, iter>, _some_struct_t>::type const_iterator_type; |
Re: Generics <> Templates ?
Ich halte mal fest (hoffentlich hab ich das richtig verstanden):
Templates: Untypisierte Vorlagen, die vor der Compilierung durch einen Prä-Prozessor typisiert werden, so das der Compiler zu jeder typisierten Variante entsprechenden Code hat und compilieren kann. Generics: Untypisierte "Vorlage", die der Compiler compilieren kann. Diese werden aber erst zur Laufzeit entsprechend typisiert werden. Was aber auch bedeutet, das eine Typprüfung erst zur Laufzeit stattfinden kann, da erst da der Typ bekannt ist. Ist das so richtig ? @Elvis Ähm..also...das Beispiel....naja...das könnte man auch heute schon ohne Generics implementieren :) |
Re: Generics <> Templates ?
Zitat:
Ein schönes Beispiel dafür sind typisierte Listen:
Delphi-Quellcode:
Diese Klasse wir fertig übersetzt in Maschinensprache genau so aussehen wie TObjectList - die Elemente sind TObjects. Sie hat jedoch den Vorteil, dass man Eigenschaften wie "Items" und Methoden wie "Add" wirklich typisieren kann.
type
TGenericList<T> = class where T is TObject private function GetItem(Index: Integer): T; procedure SetItem(Index: Integer; const Value: T); public procedure Add(AValue: T); property Items[Index: Integer]: T read GetItem write SetItem; end; Wenn du allerdings einen Typen davon ableitest mit (ich kenne mich mit der Chrome-Syntax nicht aus, kann also falsch sein)
Delphi-Quellcode:
dann kann der Compiler schon prüfen, dass nur TWinControl-Objekte darin abgelegt werden. Dann sieht es so aus als ob du den Typ deklariert hättest mit <TWinControl> statt <T>:
var
WinControlList = TGenericList<TWinControl>;
Delphi-Quellcode:
... und der Compiler kann dann beim Übersetzen schon einen Typfehler anmeckern, wenn du irgendetwas anderes als ein TWinControl zu deiner Liste hinzufügen willst.
type
// Geht so natürlich nicht TGenericList<TWinControl> = class private function GetItem(Index: Integer): TWinControl; procedure SetItem(Index: Integer; const Value: TWinControl); public procedure Add(AValue: TWinControl); property Items[Index: Integer]: TWinControl read GetItem write SetItem; end; |
Re: Generics <> Templates ?
Aaaaah.....jetzt wirds hell :)
Das heißt man hat die "Funktionalität" von Templates ohne den ganzen zusätzlichen Code, den ein Prä-Prozessor verzapft, und trotzdem die gewohnte pascalsche Typsicherheit :) |
Re: Generics <> Templates ?
Zitat:
Code:
Das ist eine Klasse Factorial, die zur Kompilierzeit (!) eine Fakultät berechnet. Wird ungefähr so verwendet:
template< int i >
class Factorial{ public: enum {Value = i * Factorial<i - 1>::Value}; }; class Factorial< 1 >{ public: enum {Value = 1}; };
Code:
Ob man das braucht, ist eine andere Frage, aber das ist etwas, das man mit Generics allgemein nicht machen kann.
int n = Factorial<10>::Value;
|
Re: Generics <> Templates ?
Zitat:
Der Code-Bloat läßt sich auch bei Templates vermeiden, indem man Templates intern über typagnostische Klassen implementiert. Leider ist das für die STL nicht so einfach, da diese laut Standard (auch bezüglich der Allokatoren) eine gewaltige Flexibilität bieten muß. Zitat:
Code:
Das stammt aus meiner Implementation eines Iterator-Wrappers, der, abhängig davon, ob value_type bereits const ist, sowohl als iterator als auch als const_iterator genutzt werden kann. Allerdings muß ein iterator sowohl aus iterator- als auch aus const_iterator-Objekten konstruierbar sein - und wenn der Iterator dann schon ein const_iterator ist, gibts den Kopierkonstruktor zweimal. Um das zu vermeiden, nimmt der zweite Konstruktor, je nachdem, ob value_type das gleiche wie const value_type ist, entweder einen const_iterator oder eine nichtöffentliche Struktur entgegen.
template <typename T,
typename Base, int direction = UCL_ITERWRAP_FORWARD, typename iter = T*, typename tag = std::bidirectional_iterator_tag> // only for derived classes class BiDiIterator : public std::iterator <tag, T> { typedef std::iterator <tag, T> _iterBase; private: struct _some_struct_t {}; public: typedef typename IfThenElseType <TypesAreEqual <T, const T>::result, BiDiIterator <const T, Base, direction, iter>, _some_struct_t>::type const_iterator_type; typedef BiDiIterator iterator_type; ... public: BiDiIterator (const iterator_type& rhs) : _ptr (rhs._ptr) {} BiDiIterator (const const_iterator_type& rhs) : _ptr (rhs._getWrappedIterator ()) {} ... }; Weiter kann der Iterator über einen Template-Parameter entweder als (forward-)iterator oder reverse_iterator eingesetzt werden. Auch verwendet er je nachdem, ob der Iteratortyp, den er wrappt, einfach nur ein Zeiger auf den value_type (der immer vorwärts iteriert) oder ein echter reverse_iterator ist, dessen Inkrement- oder Dekrementoperator. Mach das mal ohne TMP ;) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:19 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz