Automatischer Konstruktor in explizit instanziierter Klassenvorlage

8

Ich habe eine template<bool VAR> struct Obj Vorlage in einer Header-Datei ( obj.h ) mit explizitem automatischen Move-Konstruktor ( = default ) deklariert.

%Vor%

Die Memberfunktion der Vorlage wird in einer anderen Datei ( obj.cpp ) mit expliziter Instanziierung der Vorlage definiert:

%Vor%

Diese Vorlage wird dann aus der Hauptdatei ( main.cpp ) verwendet:

%Vor%

Die .cpp s werden dann kompiliert und mit dem folgenden Makefile verknüpft:

%Vor%

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 .
  • lösche
  • Wenn ich extern template aus der Kopfzeile entferne, kompiliert das Programm.
  • Wenn ich int anstelle von std::vector<int> für den Typ member verwende, kompiliert das Programm auch.
  • cppreference.com behauptet, dass "der Compiler einen Move-Konstruktor als nicht explizite inline deklariert öffentliches Mitglied seiner Klasse ". Der manuell definierte 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.)

    
Jan Špaček 13.08.2016, 06:35
quelle

3 Antworten

4

Interessant. Ich denke, dein Code ist korrekt, weil:

Ihr vorausgesetzter Move-Konstruktor ist implizit inline wegen:

[dcl.fct.def.default] / 5 :

  

... 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

%Vor%

Dieser wird nur im -O0 Modus verwendet. Im Produktionscode wird stattdessen die Inline-Version verwendet.

    
rustyx 13.08.2016, 09:52
quelle
2

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:

JVApen 13.08.2016 08:26
quelle
0

Es ist möglich, dass Sie einen Compiler-Fehler treffen.

Siehe Ссылка .

Ich treffe ähnliches Verhalten auf CLang, kann aber keinen Fehlerbericht finden.

    
Dave Dopson 18.09.2017 00:22
quelle

Tags und Links