Ich habe eine template<bool VAR> struct Obj
Vorlage in einer Header-Datei ( obj.h
) mit explizitem automatischen Move-Konstruktor ( = default
) deklariert.
Die Memberfunktion der Vorlage wird in einer anderen Datei ( obj.cpp
) mit expliziter Instanziierung der Vorlage definiert:
Diese Vorlage wird dann aus der Hauptdatei ( main.cpp
) verwendet:
Die .cpp
s werden dann kompiliert und mit dem folgenden Makefile
verknüpft:
Ich erhalte jedoch einen Linker-Fehler: undefined reference to 'Obj<true>::Obj(Obj<true>&&)'
- der Compiler hat den Konstruktor offenbar nicht instanziiert.
Obj<true>::member_fun()
ist definiert und das Programm ist tatsächlich erfolgreich verlinkt, wenn ich den Verweis auf den Move-Konstruktor aus main.cpp
. extern template
aus der Kopfzeile entferne, kompiliert das Programm. int
anstelle von std::vector<int>
für den Typ member
verwende, kompiliert das Programm auch. Obj(int)
-Konstruktor ist jedoch ebenfalls inline, wird jedoch korrekt instanziiert. (Ich habe diesen Fehler mit Clang in einem Projekt erhalten, das gut mit GCC kompiliert wurde, also dachte ich, das wäre ein Clang-Bug. Wenn ich jedoch das Problem auf diesen einfachen Fall reduzierte, sowohl GCC 5.4.0 als auch Clang 3.8.0 produzieren die gleichen Ergebnisse.)
Interessant. Ich denke, dein Code ist korrekt, weil:
Ihr vorausgesetzter Move-Konstruktor ist implizit inline
wegen:
... Eine vom Benutzer bereitgestellte explizit voreingestellte Funktion (d. h. explizit nach der ersten Deklaration voreingestellt) wird an dem Punkt definiert, an dem Es ist explizit voreingestellt.
Und [class.mfct] / 1 :
In seiner Klasse kann eine Memberfunktion definiert werden ([dcl.fct.def]) Definition, in diesem Fall ist es eine Inline-Member-Funktion ([dcl.fct.spec])
Und ist somit von der expliziten Template-Instantiierung gemäß [temp.explicit] / 10 ( Betonung meiner):
Mit Ausnahme von Inline-Funktionen und Variablen , Deklarationen mit Typen abgeleitet von ihrem Initialisierer oder Rückgabewert ([dcl.spec.auto]), Konstante Variablen von Literaltypen, Variablen von Referenztypen und Klassenvorlagenspezialisierungen, explizite Instanziierungsdeklarationen haben die Wirkung, die implizite Instantiierung der Entität, auf die sie sich beziehen. [Hinweis: Die Absicht ist, dass ein Inline Funktion, die Gegenstand einer expliziten Instanziierungserklärung ist wird immer noch implizit instanziiert, wenn odr-used ([basic.def.odr]) so dass der Körper für Inlining in Betracht gezogen werden kann, aber dass nein Out-of-Line-Kopie der Inline-Funktion würde in der erstellt werden Übersetzungseinheit. - Endnote]
Wenn Sie einen anderen Optimierungsmodus als -O0
ausprobieren, verschwindet das Problem tatsächlich.
-O0
ist ein spezieller Modus, in dem Inline-Funktionen nicht inline sind. Aber es sollte nicht wichtig sein, der Compiler muss in diesem Fall den inline
defaulted move-constructor wie mit dem anderen Konstruktor erzeugen.
Für mich sieht das also wie ein Compilerfehler aus. Siehe auch LLVM # 22763 und GCC # 60796 .
Ich sehe mindestens zwei mögliche Problemumgehungen:
Lösung 1
Benutze extern template ...
nicht für jetzt (die Übersetzungszeiten werden leiden, aber ansonsten keine große Sache).
Lösung 2
Machen Sie den Compiler zum Generieren des unterdrückten Konstruktors in obj.cpp
Dieser wird nur im -O0
Modus verwendet. Im Produktionscode wird stattdessen die Inline-Version verwendet.
Es gibt nicht viele Informationen zu diesem Thema im Internet. Wenn ich einige Quellen betrachte, würde ich folgendes feststellen:
extern template
sollte implizite Instanziierungen verhindern, obwohl in allen Beispielen die explicit
Instantiierung nicht diese extern template
-Definition hat.
Nach dem, was ich lesen kann, insbesondere auf dem Vorschlag und der GCC-Mailingliste (siehe Referenzen unten), verhindert extern template
nicht implicit
Instanziierungen, obwohl ALLE Instanzen der Vorlage. Dies würde Ihre explicit
Instanziierung einschließen.
Wenn eine Entität Gegenstand einer expliziten Instanziierung ist Deklaration und eine explizite Instanziierungsdefinition In derselben Übersetzungseinheit muss die Definition der Erklärung folgen. - John Spicer auf der GCC-Mailingliste
Daraus würde ich schließen, dass Sie die extern template
in der Übersetzungseinheit entfernen sollten, in der die explizite Instanziierung erfolgen soll.
Referenzen:
Es ist möglich, dass Sie einen Compiler-Fehler treffen.
Siehe Ссылка .
Ich treffe ähnliches Verhalten auf CLang, kann aber keinen Fehlerbericht finden.