Nach meinem Verständnis, wenn Sie beispielsweise std::vector<int>
und std::vector<float>
haben, erstellt der Compiler zwei Klassen, eine für jeden Typ. Obwohl Sie die Menge an geschriebenem Code reduzieren, reduzieren Sie die Größe der ausführbaren Datei nicht (korrigieren Sie mich, wenn ich falsch liege).
Stimmt das auch, wenn der Typ ein Zeiger ist? Würde zum Beispiel die Instanziierung eines std::vector<SomeClass*>
und eines std::vector<SomeOtherClass*>
notwendigerweise dazu führen, dass der Compiler separaten Code für jede der beiden Instanziierungen generiert?
Der Compiler instanziiert so viele Klassen aus der Vorlage, wie Ihr Programm verwendet. Code, der in Ihrer ausführbaren Datei generiert wird, unterscheidet sich geringfügig von den Klassen in Ihrem Programm.
In der Praxis werden die meisten Operationen in vector
inline ausgeführt. Daher ändert sich die Größe der ausführbaren Datei wahrscheinlich nicht sehr, je nachdem, wie viele verschiedene Klassen aus dieser Vorlage instanziiert werden, da der Großteil der Code-Größe pro Funktionsaufruf-Site und nicht pro bestimmter Klasse besteht. Soweit dies jedoch von der Anzahl der Instanziierungen abhängt, sind vector<SomeClass*>
und vector<SomeOtherClass*>
verschiedene Klassen.
Wenn Sie eine explizite Instanziierung von vector
durchführen, dann all werden die Elementfunktionen für die Klasse generiert. Sie werden diesen Unterschied in der Code-Größe wahrscheinlich sehen, wenn Sie danach suchen. Normalerweise werden Template-Klassen nicht explizit instanziiert, sodass nur die von Ihnen verwendeten Member-Funktionen generiert werden.
Dies ist eine implementierungsabhängige Als-ob-Optimierung und ist somit erlaubt!
Tatsächlich muss dies nicht einmal vom Compiler erledigt werden. Die Standardbibliothek kann auf diese Weise implementiert werden. Beispielsweise könnte eine Implementierung std::is_pointer
verwenden und dann alles auf eine einzelne void*
-basierte Implementierung verschieben. (Dies ist das Thin Template idiom). In der Tat scheint dies auf der Bibliotheksseite praktikabler zu sein als der Code des Compilers nach der Instanziierung, aber das ist auch möglich.
Es ist zulässig, dass der Compiler einen einzigen Satz Code generiert, der beide Instanzen einer Vorlage implementiert, vorausgesetzt, das resultierende Verhalten ist korrekt. Dies kann bei Zeigertypen und Nicht-Zeigertypen vorkommen. Es kann unabhängig für jede Routine (a.k.a. "Methode") in einer Vorlagenklasse vorkommen.
Es kann schwierig sein zu bestimmen, wann dies passieren kann, und ein Compiler erkennt möglicherweise Möglichkeiten dazu oder auch nicht.
Wenn beispielsweise eine Routine lediglich eine Klasse kopiert, wie es normalerweise ein Zuweisungsoperator tut, dann kann es möglich sein, denselben Code für jede Instanziierung der Vorlage zu verwenden, in der die Klassendaten die gleiche Größe haben. Der Code zum Hinzufügen von zwei int
-Objekten kann mit dem Code identisch sein, der bei einigen Prozessoren zu unsigned int
-Objekten hinzugefügt wird.
Ja, du hast Recht. Es gibt Möglichkeiten, die Duplizierung zu mindern, siehe Folien 18-26 in meiner Präsentation Diätvorlagen .
An einem Punkt war "allgemeines Wissen", dass std::vector<T*>
und std::vector<U*>
beide als dünne Wrapper um eine vector_impl<void*>
Spezialisierung implementiert werden könnten, so dass sie den gleichen generierten Code teilen, wo es möglich ist (darauf bezog ich mich) wie hissen in meinen Dias), aber ich glaube nicht, dass moderne std :: lib Implementierungen diese Optimierung tatsächlich tun. Natürlich tut libstdc ++ nicht.
Der Compiler erstellt keine zwei Klassen. Vielmehr sind std::vector<int>
und std::vector<float>
zwei verschiedene Klassen. Verwechseln Sie keine Klassen und Vorlagen - das sind Kernkonzepte der Sprache! Und in ähnlicher Weise sind std::vector<SomeClass *>
und std::vector<SomeOtherClass *>
zwei verschiedene Klassen, die Ihre Frage beantworten sollten.