[c++] Warum können Vorlagen nur in der Header-Datei implementiert werden?


Answers

Viele richtige Antworten hier, aber ich wollte dies (zur Vollständigkeit) hinzufügen:

Wenn Sie am Ende der cpp-Datei für die Implementierung eine explizite Instanziierung aller Typen vornehmen, mit denen die Vorlage verwendet wird, kann der Linker sie wie gewöhnlich finden.

Bearbeiten: Hinzufügen eines Beispiels für die explizite Vorlageninstanziierung. Wird verwendet, nachdem die Vorlage definiert wurde und alle Mitgliedsfunktionen definiert wurden.

template class vector<int>;

Dies wird die Klasse und alle ihre Mitgliedsfunktionen (nur) instanzieren (und somit dem Linker verfügbar machen). Eine ähnliche Syntax funktioniert für Template-Funktionen. Wenn Sie also Überladungen von Nicht-Member-Operatoren haben, müssen Sie dies möglicherweise auch tun.

Das obige Beispiel ist ziemlich nutzlos, da Vektor vollständig in Headern definiert ist, außer wenn eine allgemeine Include-Datei (vorkompilierter Header?) Den extern template class vector<int> damit er nicht in allen anderen (1000?) Dateien instanziiert wird das verwenden Vektor.

Question

Zitat aus der C ++ Standard-Bibliothek: ein Tutorial und Handbuch :

Die einzige Möglichkeit, Vorlagen im Moment zu verwenden, besteht darin, sie mithilfe von Inline-Funktionen in Header-Dateien zu implementieren.

Warum ist das?

(Klarstellung: Header-Dateien sind nicht die einzige portable Lösung. Aber sie sind die bequemste portable Lösung.)




Obwohl Standard-C ++ keine solche Anforderung hat, erfordern einige Compiler, dass alle Funktions- und Klassenvorlagen in jeder verwendeten Übersetzungseinheit verfügbar gemacht werden müssen. Für diese Compiler müssen die Körper der Vorlagenfunktionen in einer Header-Datei verfügbar gemacht werden. Um dies zu wiederholen: Das bedeutet, dass diese Compiler nicht zulassen, dass sie in Nicht-Header-Dateien wie CPP-Dateien definiert werden

Es gibt ein Export- Schlüsselwort, das dieses Problem abschwächen soll, aber es ist noch lange nicht tragbar.




Vorlagen müssen vom Compiler instanziiert werden, bevor sie tatsächlich in Objektcode kompiliert werden. Diese Instanziierung kann nur erreicht werden, wenn die Template-Argumente bekannt sind. Stellen Sie sich nun ein Szenario vor, in dem eine Template-Funktion in ah deklariert, in a.cpp definiert und in b.cpp . Wenn a.cpp kompiliert wird, ist nicht unbedingt bekannt, dass die bevorstehende Kompilierung b.cpp eine Instanz der Vorlage benötigt, geschweige denn welche spezifische Instanz. Für mehr Header- und Quelldateien kann die Situation schnell komplizierter werden.

Man kann argumentieren, dass Compiler intelligenter gemacht werden können, um für alle Anwendungen des Templates vorauszusehen, aber ich bin mir sicher, dass es nicht schwierig wäre, rekursive oder anderweitig komplizierte Szenarien zu erstellen. AFAIK, Compiler tun solche Vorausschauen nicht. Wie Anton betonte, unterstützen einige Compiler explizite Exportdeklarationen von Template-Instanziierungen, aber nicht alle Compiler unterstützen sie (noch?).




Dies bedeutet, dass die am besten portierbare Methode zum Definieren von Methodenimplementierungen von Vorlagenklassen darin besteht, sie innerhalb der Vorlagenklassendefinition zu definieren.

template < typename ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};



Der Compiler generiert Code für jede Vorlageninstanziierung, wenn Sie während des Kompilierungsschritts eine Vorlage verwenden. Beim Kompilieren und Verknüpfen werden CPP-Dateien in reinen Objekt- oder Maschinencode konvertiert, der in ihnen Referenzen oder nicht definierte Symbole enthält, da die .h-Dateien, die in Ihrer main.cpp enthalten sind, keine Implementierung YET haben. Diese sind bereit, mit einer anderen Objektdatei verknüpft zu werden, die eine Implementierung für Ihre Vorlage definiert und somit eine vollständige a.out-Programmdatei enthält. Da Templates jedoch im Kompilierungsschritt verarbeitet werden müssen, um Code für jede Template-Instanziierung zu generieren, die Sie in Ihrem Hauptprogramm ausführen, hilft die Verknüpfung nicht, da Sie die Datei main.cpp in main.o kompilieren und dann Ihre Vorlage .cpp kompilieren in template.o und dann wird das Verknüpfen nicht den Vorlagenzweck erreichen, da ich verschiedene Template-Instanziierung mit der gleichen Template-Implementierung verknüpfe! Und Templates sollen das Gegenteil tun, dh EINE Implementierung haben, aber viele verfügbare Instanzen über die Verwendung einer Klasse erlauben.

Bedeutung typename T get wird während des Kompilierungsschrittes ersetzt, nicht der Verknüpfungsschritt. Wenn ich also versuche, eine Vorlage zu kompilieren, ohne T als konkreten typename T zu ersetzen, wird es nicht funktionieren, da dies die Definition von Templates ist Bei der Meta-Programmierung geht es darum, diese Definition zu verwenden.




Das ist genau richtig, weil der Compiler wissen muss, um welchen Typ es sich handelt. Also Template-Klassen, Funktionen, Enums, etc .. muss auch in der Header-Datei implementiert werden, wenn es veröffentlicht werden soll oder Teil einer Bibliothek (statisch oder dynamisch), da Header-Dateien NICHT kompiliert werden im Gegensatz zu den c / cpp-Dateien sind. Wenn der Compiler den Typ nicht kennt, kann er nicht kompiliert werden. In .Net kann es, weil alle Objekte von der Object-Klasse abgeleitet sind. Dies ist nicht .Net.




Related