Müssen Template-Klassenmitgliedsfunktionsimplementierungen immer in die Header-Datei in C ++ gehen? [Duplikat]

7

Wenn ich eine Klasse erstelle, erzeuge ich normalerweise eine Kopfzeile und eine Quelle für diese Klasse. Ich habe gehört, dass Sie bei einer Template-Klasse die Funktionsimplementierung in den Header setzen müssen. Ich habe versucht, es auf beide Arten zu machen und habe Kompilierungsfehler auf die erste Art und Weise bekommen. Der zweite Weg hat gut funktioniert. Wie auch immer, ich mag es, meinen Code in Kopf- und Quelldateien zu organisieren, also ist es möglich, die Funktionsimplementierungen in eine Quelldatei zu stellen? (Vielleicht erfordert es spezielle Kompilierungsflags oder Syntax?) Oder sollte ich nur em in der Kopfzeile halten?

Danke!

    
cat pants 28.12.2011, 23:39
quelle

5 Antworten

14

Im Allgemeinen muss sich der gesamte Vorlagencode in einer Header-Datei befinden, da der Compiler den vollständigen Typ zum Zeitpunkt der Instanziierung kennen muss.

Wie Aaron unten sagt, ist es möglich, die Implementierungsdetails in eine .cpp -Datei in dem speziellen Fall zu stellen, in dem Sie alle möglichen Typen kennen, mit denen die Vorlage instanziiert wird, und sie explizit mit diesen Typen instanziieren. Sie erhalten dann einen Linker-Fehler, wenn die Vorlage irgendwo in Ihrem Code mit einem anderen Typ instanziiert wird.

Eine allgemein übliche Lösung für eine zumindest visuell getrennte Schnittstelle von der Implementierung besteht darin, alle Implementierungen in eine .inc (oder .tcc oder .ipp ) - Datei zu schreiben und sie am Ende der Header-Datei einzufügen / p>

Beachten Sie, dass die Syntax zum Übergeben von Vorlagenklassenmitgliedern außerhalb der Klassendefinition (ob Sie die spezifische Lösung oder das allgemeine verwenden) etwas mühsam ist. Sie müssen schreiben:

%Vor%     
Andreas Magnusson 28.12.2011, 23:47
quelle
13

( Bearbeitet : Dies ist eine etwas robustere Version, die es ermöglicht, die Implementierung in eine separate .o-Datei zu kompilieren. Die Vorlage sollte am Ende der cpp-Datei der Implementierung explizit instanziiert werden war nur ein Problem für g ++.)

Sie müssen die Implementierungen nicht in den Header setzen, wenn Ihnen bekannt ist, welche Templates instanziiert werden und sie in der Header Implementierungsdatei auflisten können. Wenn Sie beispielsweise wissen, dass Sie nur int und std::string verwenden, können Sie dies in die Header-Datei einfügen:

%Vor%

und setzen Sie die Implementierung von f () in eine normale test.cpp-Datei:

%Vor%

In den letzten beiden Zeilen werden die Vorlagenklassen explizit instanziiert. Es ist besser, dies an das Ende der Implementierungsdatei zu setzen, nachdem es die Implementierungen der Member-Funktionen gesehen hat. Dann können Sie es in eine .o-Datei g++ -c test.cpp kompilieren. Diese Datei test.o enthält vollständige Implementierungen von Test<int> und Test<string> und kann problemlos mit dem Rest Ihrer Anwendung verknüpft werden.

Es funktioniert, aber ist es eine gute Idee? Es hängt vom Kontext ab. In vielen Fällen funktioniert das sehr gut. Wenn Sie eine Vorlage für die interne Verwendung in Ihrem Projekt schreiben, wissen Sie, welche Vorlagen instanziiert werden und welche nicht. Wenn Sie stattdessen der Öffentlichkeit etwas zur Verfügung stellen, das sehr flexibel sein muss, müssen Sie die Implementierungen in die Header-Datei aufnehmen.

Ein Tipp: Auch wenn es für den öffentlichen Konsum ist, werfen Sie einen Blick auf die Methoden und sehen Sie, ob es Methoden gibt, deren Parameter und Rückgabetyp unabhängig von den Template-Parametern sind. Wenn ja, könnten Sie sie als (reine) virtuelle Funktionen in eine Basisklasse einfügen. Diese Basisklasse verwendet keine Vorlagen und daher können Sie diese Base* in vielen Ihrer Anwendungen verwenden ( template <class A> class Test : public Base { ... , so dass Sie die Reichweite des Vorlagencodes in Ihrer gesamten Anwendung einschränken können. Ich fand das in letzter Zeit nützlich Wenn ein Großteil des zugrunde liegenden Verhaltens und der Konstruktion einer Klasse von den Vorlagenparametern abhing, war die Schnittstelle zu dem bereits erstellten Objekt nicht von den Vorlagenparametern abhängig.

    
Aaron McDaid 29.12.2011 00:01
quelle
5

Um die ursprüngliche Frage zu beantworten: Nein, die Definitionen von [member] -Funktionsvorlagen müssen nicht in den Header gehen. Der Compiler muss jedoch die Definition sehen, um die Vorlage zu instanziieren. Bei Templates, die mit vielen verschiedenen Typen instanziiert sind, sollen die Templates implizit instanziiert werden, wenn sie verwendet werden. Dies ist z.B. der Fall für Klassenvorlagen wie std::vector<...> und für Funktionsvorlagen wie std::copy(...) . In diesem Fall ist es höchst unpraktisch, die Template-Definition von ihrer Deklaration zu trennen, obwohl ich persönlich die Definitionen in eine separate Datei am Ende der Header-Datei lege.

Für Templates, die nur mit ein paar Typen wie den Stream-Klassen oder std::basic_string<...> instanziiert werden, ist es oft besser, die Funktionsvorlagen in einer separaten headerartigen Datei zu definieren, die nur in Implementierungsdateien enthalten ist, die sie explizit instanziieren. Auf diese Weise wird der Aufwand für die Instanziierung der Vorlage nur einmal und nicht in jeder Übersetzungseinheit, die sie verwendet, verbraucht. Speziell für die Stream-Klassen macht dies einen großen Unterschied (in erster Linie für Kompilier- und Link-Zeit, aber auf einigen Systemen auch für ausführbare Größe). ... und ich bin ziemlich sicher, dass kaum jemand sich die Mühe gemacht hat, die Stream-Klassen mit anderen Zeichentypen als char und wchar_t zu verwenden (Hinweis: es ist nicht trivial, die verschiedenen Facetten zu implementieren) und anwesend in std::locale ). Die Technik der expliziten Instanziierung von Vorlagen funktioniert auch dann gut, wenn nur eine begrenzte Anzahl von Typen vorhanden ist, mit denen die Vorlage arbeiten soll.

    
Dietmar Kühl 29.12.2011 01:37
quelle
4

Während es technisch gesehen der Fall ist, dass Sie Ihre Implementierung von Ihrer Schnittstelle trennen können, wird die Syntax für Vorlagen so nervig, dass Sie sie wiederholt eingeben. Ich würde Ihnen dringend empfehlen, die Implementierung in Ihre Klasse zu legen riechen.

%Vor%

wäre gewesen:

%Vor%

Stellen Sie sich nun vor, Sie möchten einen weiteren Template-Parameter hinzufügen (wie im Folgenden). Jetzt müssen Sie die Vorlagenargumentlisten an n Stellen statt nur an einer ändern.

%Vor%

Wenn Sie nicht einen guten Grund haben, es nicht zu tun, ist es viel einfacher für Sie, einfach alles in die Klasse zu legen.

    
Bowie Owens 29.12.2011 00:09
quelle
2

Template-Implementierungen müssen zur Kompilierungszeit bekannt sein. Das bedeutet, dass die Implementierungen für den Compiler sichtbar sein müssen. Es gibt keine Möglichkeit, dies zu umgehen.

Wenn Sie Implementierungsdetails geheim halten wollen, gibt es keine Möglichkeit, dies zu tun. Sie können natürlich Ihren Code verschleiern, aber das ist kein großes Hindernis.

Wenn es nur um die Codeorganisation geht, können Sie eine separate Include-Datei mit den Implementierungen erstellen und sie am Ende Ihrer Hauptkopfzeile einfügen (ähnlich wie Andreas 'Vorschlag).

    
Luchian Grigore 28.12.2011 23:54
quelle

Tags und Links