Abbildung 1: Funktionsvorlagen
TemplHeader.h
template<typename T>
void f();
TemplCpp.cpp
template<typename T>
void f(){
//...
}
//explicit instantation
template void f<T>();
Main.cpp
#include "TemplHeader.h"
extern template void f<T>(); //is this correct?
int main() {
f<char>();
return 0;
}
Ist dies die richtige Verwendung extern template
oder verwende ich dieses Schlüsselwort nur für Klassenvorlagen wie in Abbildung 2?
Abbildung 2: Klassenvorlagen
TemplHeader.h
template<typename T>
class foo {
T f();
};
TemplCpp.cpp
template<typename T>
void foo<T>::f() {
//...
}
//explicit instantation
template class foo<int>;
Main.cpp
#include "TemplHeader.h"
extern template class foo<int>();
int main() {
foo<int> test;
return 0;
}
Ich weiß, dass es gut ist, all dies in eine Header-Datei zu packen, aber wenn wir Vorlagen mit denselben Parametern in mehreren Dateien instanziieren, haben wir mehrere gleiche Definitionen und der Compiler entfernt sie alle (außer einer), um Fehler zu vermeiden. Wie benutze ich extern template
? Können wir es nur für Klassen verwenden oder können wir es auch für Funktionen verwenden?
Außerdem können Abbildung 1 und Abbildung 2 zu einer Lösung erweitert werden, bei der sich Vorlagen in einer einzelnen Header-Datei befinden. In diesem Fall müssen wir das extern template
Schlüsselwort verwenden, um mehrere gleiche Instanziierungen zu vermeiden. Gilt das auch nur für Klassen oder Funktionen?
extern template class foo<int>();
scheint wie ein Fehler.()
der externen Leitung "erwartete unqualifizierte ID" steht . Sowohl Ihr Buch als auch Ihr Visual Studio sind falsch. Versuchen Sie, einen standardkonformeren Compiler wie g ++ oder clang zu verwenden, und Sie werden das Problem sehen.Antworten:
Sie sollten nur verwenden
extern template
, um den Compiler zu zwingen, eine Vorlage nicht zu instanziieren, wenn Sie wissen, dass sie an einer anderen Stelle instanziiert wird. Es wird verwendet, um die Kompilierungszeit und die Größe der Objektdatei zu reduzieren.Beispielsweise:
Dies führt zu folgenden Objektdateien:
Wenn beide Dateien miteinander verknüpft sind,
void ReallyBigFunction<int>()
wird eine verworfen, was zu einer Verschwendung von Kompilierungszeit und Objektdateigröße führt.Um Kompilierungszeit und Objektdateigröße nicht zu verschwenden, gibt es ein
extern
Schlüsselwort, mit dem der Compiler keine Vorlagenfunktion kompiliert. Sie sollten dies genau dann verwenden, wenn Sie wissen, dass es in derselben Binärdatei an einer anderen Stelle verwendet wird.Wechseln
source2.cpp
zu:Führt zu folgenden Objektdateien:
Wenn beide miteinander verknüpft werden, verwendet die zweite Objektdatei nur das Symbol aus der ersten Objektdatei. Keine Notwendigkeit zum Verwerfen und keine verschwendete Kompilierungszeit und Objektdateigröße.
Dies sollte nur innerhalb eines Projekts verwendet werden, wie in Zeiten, in denen Sie eine Vorlage
vector<int>
mehrmals verwenden, sollten Sie sieextern
in allen außer einer Quelldatei verwenden.Dies gilt auch für Klassen und Funktionen als eine und sogar für Vorlagenelementfunktionen.
quelle
Wikipedia hat die beste Beschreibung
Die Warnung:
nonstandard extension used...
Microsoft VC ++ hatte bereits seit einigen Jahren eine nicht standardmäßige Version dieser Funktion (in C ++ 03). Der Compiler warnt davor, um Portabilitätsprobleme mit Code zu vermeiden, die auch auf verschiedenen Compilern kompiliert werden mussten.
Schauen Sie sich das Beispiel auf der verlinkten Seite an, um festzustellen, dass es ungefähr genauso funktioniert. Sie können davon ausgehen, dass die Nachricht bei zukünftigen Versionen von MSVC nicht mehr angezeigt wird, außer natürlich, wenn Sie gleichzeitig andere nicht standardmäßige Compiler-Erweiterungen verwenden.
quelle
std::vector
(ziemlich sicher, dass alle dies tun),extern
hat dies keine Auswirkung.extern template
wird nur benötigt, wenn die Vorlagendeklaration vollständig istDies wurde in anderen Antworten angedeutet, aber ich denke, es wurde nicht genug Wert darauf gelegt.
Dies bedeutet, dass in den OP-Beispielen dies
extern template
keine Auswirkung hat, da die Vorlagendefinitionen in den Headern unvollständig waren:void f();
: nur Erklärung, kein Körperclass foo
: deklariert Methodef()
, hat aber keine DefinitionDaher würde ich empfehlen,
extern template
in diesem speziellen Fall nur die Definition zu entfernen : Sie müssen sie nur hinzufügen, wenn die Klassen vollständig definiert sind.Beispielsweise:
TemplHeader.h
TemplCpp.cpp
Main.cpp
Kompilieren und Anzeigen von Symbolen mit
nm
:Ausgabe:
und dann sehen
man nm
wir, dassU
dies undefiniert bedeutet, so dass die Definition nurTemplCpp
wie gewünscht beibehalten wurde.All dies läuft auf den Kompromiss vollständiger Header-Deklarationen hinaus:
extern template
jeden Einschluss hinzufügen , was Programmierer wahrscheinlich vergessen werdenWeitere Beispiele hierfür finden Sie unter: Explizite Vorlageninstanziierung - wann wird sie verwendet?
Da die Kompilierungszeit in großen Projekten so wichtig ist, würde ich unvollständige Vorlagendeklarationen dringend empfehlen, es sei denn, externe Parteien müssen Ihren Code unbedingt mit ihren eigenen komplexen benutzerdefinierten Klassen wiederverwenden.
In diesem Fall würde ich zunächst versuchen, Polymorphismus zu verwenden, um das Problem der Erstellungszeit zu vermeiden, und Vorlagen nur verwenden, wenn spürbare Leistungssteigerungen erzielt werden können.
Getestet in Ubuntu 18.04.
quelle
Das bekannte Problem mit den Vorlagen ist das Aufblähen von Code, was die Folge der Generierung der Klassendefinition in jedem Modul ist, das die Spezialisierung auf Klassenvorlagen aufruft. Um dies zu verhindern, könnte man ab C ++ 0x das Schlüsselwort extern vor der Spezialisierung auf Klassenvorlagen verwenden
Die explizite Instanziierung der Vorlagenklasse sollte nur in einer einzelnen Übersetzungseinheit erfolgen, vorzugsweise in der mit Vorlagendefinition (MyClass.cpp).
quelle
Wenn Sie extern zuvor für Funktionen verwendet haben, wird für Vorlagen genau dieselbe Philosophie befolgt. Wenn nicht, kann es hilfreich sein, für einfache Funktionen nach außen zu gehen. Möglicherweise möchten Sie auch die externen Elemente in die Header-Datei einfügen und den Header bei Bedarf einfügen.
quelle