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).
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?
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.
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:
(*) 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:
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:
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:
erzeugt auch _ZN3cls1fIPKcEEvT_
.
Hinweis: Es ist mir nicht klar, ob eine explizite Spezialisierung eine andere Mangelung hätte erhalten können.
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.
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.
Tags und Links c++ templates member-functions template-specialization default-implementation