Ist es sicher, die Definition der Spezialisierung der Template-Member-Funktion (withOUT default body) in der Quelldatei zu platzieren?

8

Folgendes meine ich:

%Vor%

-

%Vor%

-

%Vor%

Das ist völlig in Ordnung, oder?

Ich fing an, das zu bezweifeln, weil ich gerade den specialization of '...' after instantiation -Fehler übersprungen habe, was für mich neu war. Also habe ich diesen Fehler "umgangen" und alles scheint jetzt gut zu funktionieren, aber trotzdem ..

Ist das wohldefiniertes Verhalten?

edit: Und das Gleiche für Nicht-Member-Template-Funktionen (vorwärts deklarierte Nicht-Member-Template-Funktionen).

    
Kiril Kirov 10.04.2014, 13:20
quelle

3 Antworten

4

Leichtathletik im Orbit hat zitiert, warum es nicht konform ist Teile aus dem Standard. Es könnte einige andere in der Nähe geben.

Ich werde versuchen, in einfacheren Worten zu erklären, was der Standard-Wortschatz bedeutet, und hoffentlich werde ich es richtig verstehen und schließlich die Linker-Fehler (oder die Abwesenheit von Fehlern) erklären:

  1. Was ist der Punkt der Instanziierung?
  2. Wie wählt der Compiler eine Spezialisierung aus?
  3. Was ist zum Zeitpunkt der Instantiierung notwendig?
  4. Warum Linkfehler?

1 / Was ist der Punkt der Instanziierung?

Der Punkt der Instantiierung einer Template-Funktion ist der Punkt, an dem sie aufgerufen wird oder auf den verwiesen wird ( &std::sort<Iterator> ), wobei all die Template-Parameter ausgegraut (*) sind.

%Vor%

Bei Vorlagen, die von anderen Vorlagen aufgerufen werden, kann es jedoch verzögert werden und daher nicht mit der genauen Aufrufseite übereinstimmen:

%Vor%

Diese Verzögerung ist sehr wichtig, da sie das Schreiben ermöglicht:

  • co-rekursive Vorlagen (dh Vorlagen, die auf einander verweisen)
  • Benutzerspezialisierungen

(*) Grob gesagt gibt es Ausnahmen wie Nicht-Template-Methoden einer Template-Klasse ...

2 / Wie wählt der Compiler eine Spezialisierung aus?

Zum Zeitpunkt der Instanziierung muss ein Compiler in der Lage sein:

  • Entscheiden Sie, welche Basisvorlage zum Aufrufen von
  • verwendet werden soll
  • und möglicherweise, welche seiner Spezialisierungen nennen

Dieses alte GotW zeigt die Probleme der Spezialisierungen ... aber kurz:

%Vor%

sind Überladungen und jede erzeugt eine eigene Familie mit möglichen Spezialisierungen, von denen sie die Basis sind.

%Vor%

ist eine Spezialisierung von 1 und

%Vor%

ist eine Spezialisierung von 2.

Um den Funktionsaufruf aufzulösen, wählt der Compiler zuerst die beste Überladung aus, ignoriert dabei die Vorlagenspezialisierungen . Wenn er eine Template-Funktion ausgewählt hat, prüft er, ob eine Spezialisierung möglich ist besser anwenden.

3 / Was ist zum Zeitpunkt der Instantiierung notwendig?

Aus der Art und Weise, wie ein Compiler den Aufruf auflöst, verstehen wir warum der Standard spezifiziert, dass jede Spezialisierung vor ihrem ersten Instanziierungspunkt deklariert werden sollte. Ansonsten würde es einfach nicht berücksichtigt.

So, am Punkt der Instantiierung, muss man schon gesehen haben:

  • eine Deklaration der zu verwendenden Basisvorlagenfunktion
  • eine Erklärung der zu wählenden Spezialisierung, falls vorhanden

Aber was ist mit der Definition?

Es wird nicht benötigt. Der Compiler geht davon aus, dass er später entweder in der TU oder von einer anderen TU bereitgestellt wird.

Hinweis: Es belastet den Compiler, weil es bedeutet, dass es sich an alle impliziten Instanzen erinnert, die es gefunden hat und für die es keinen Funktionskörper ausgeben konnte, damit es endlich (endlich) auf die Definition trifft ) alle notwendigen Codes für alle Spezialisierungen ausstellen. Ich frage mich, warum dieser spezielle Ansatz ausgewählt wurde, und frage mich auch, warum selbst bei Fehlen einer extern -Deklaration die TU mit undefinierten Funktionskörpern enden könnte.

4 / Warum ein Linker-Fehler?

Da keine Definition zur Verfügung gestellt wird, vertraut gcc darauf, dass Sie es später bereitstellen und gibt einfach einen Aufruf an ein unaufgelöstes Symbol aus. Wenn Sie eine Verbindung mit einer anderen TU herstellen, die dieses Symbol bereitstellt, ist alles in Ordnung, andernfalls erhalten Sie einen Linker-Fehler.

Da gcc dem Itanium ABI folgt, können wir einfach nachsehen, wie es die Symbole verändert. Es stellt sich heraus, dass der ABI keinen Unterschied in der Mangelung von Spezialisierungen und impliziten Instanziierungen macht, also

%Vor%

ruft _ZN3cls1fIPKcEEvT_ (was als void cls::f<char const*>(char const*) abruft) und die Spezialisierung auf:

%Vor%

erzeugt auch _ZN3cls1fIPKcEEvT_ .

Hinweis: Es ist mir nicht klar, ob eine explizite Spezialisierung eine andere Mangelung hätte erhalten können.

    
Matthieu M. 02.05.2014, 14:12
quelle
3

Nein, ich denke nicht, dass es in Ordnung ist.

  

[C++11: 14/6]: Eine Funktionsvorlage, eine Elementfunktion einer Klassenvorlage oder ein statisches Datenelement einer Klassenvorlage soll in jeder Übersetzungseinheit definiert sein, in der sie implizit instanziiert wird (14.7.1), sofern die entsprechende Spezialisierung nicht explizit instanziiert wird (14.7.2) in einer Übersetzungseinheit; keine Diagnose ist erforderlich.

     

[C++11: 14.7.3/6]: Wenn eine Vorlage, eine Mitgliedervorlage oder ein Mitglied einer Klassenvorlage explizit spezialisiert ist, muss diese Spezialisierung vor der ersten Verwendung dieser Spezialisierung deklariert werden, die eine implizite Instantiierung in jeder Übersetzungseinheit bewirken würde in denen eine solche Verwendung auftritt; keine Diagnose ist erforderlich. [..]

Ehrlich gesagt kann ich nicht erklären, warum es für Sie funktioniert.

    
quelle
2

Ich denke, dass Ihr ursprünglicher Code falsch war und Ihre "Problemumgehung" auch nicht standardkonform ist (trotz der Tatsache, dass Ihr Compiler und Linker sie verarbeiten). Gute Zitate aus dem Standard wurden im antworten von @Lightness Races im Orbit Siehe auch das folgende Beispiel aus dem Standard ([temp.expl.spec] 14.7.3 / 6):

%Vor%

Ich habe meine Antwort als Community-Wiki markiert, weil es in der Tat nur ein großer Kommentar ist.

    
Constructor 23.05.2017 11:57
quelle