Ich habe Funktionen in meinem C ++ 11 Xcode-Projekt mit Vorlagen versehen, und einige von ihnen haben Spezialisierungen. Ich habe jedoch festgestellt, dass die Spezialisierungen nur in Debug-Builds aufgerufen werden; Wenn ich eine Version einbinde, werden sie ignoriert.
Ich habe erfolgreich ein sehr einfaches Beispiel erstellt:
special.h
%Vor%special.cpp
%Vor%main.cpp
%Vor%Sie können das Projekt herunterladen (bis mindestens in diesem Sommer 2013), um das Problem zu reproduzieren, wenn Sie Ich möchte es nicht selbst erstellen. Führen Sie das Projekt zunächst mit der Debug-Konfiguration aus und führen Sie es dann erneut in der Version aus. Die Ausgabe, die ich erwarte, ist:
nicht so besonders sehr speziell
Und das ist in der Tat, was ich mit der Debug-Build-Konfiguration bekomme. Mit Release bekomme ich das jedoch:
nicht so besonders nicht so besonders
Was bedeutet, dass die spezialisierte Implementierung von special::call
in special.cpp ignoriert wurde.
Warum ist das Ergebnis inkonsistent? Was muss ich tun, um sicherzustellen, dass die spezielle Funktion in Release-Builds aufgerufen wird?
Ihr Programm hat UB. Eine explizite Spezialisierung oder zumindest ihre Deklaration muss sichtbar sein, bevor sie verwendet wird. [temp.expl.spec] §6:
Wenn eine Vorlage, eine Mitgliedervorlage oder ein Mitglied einer Klassenvorlage ist explizit spezialisiert dann, dass Spezialisierung erklärt werden soll vor der ersten Verwendung dieser Spezialisierung, die ein verursachen würde implizite Instantiierung, in jeder Übersetzungseinheit in stattfinden was eine solche Verwendung vorkommt; keine Diagnose ist erforderlich.
Fügen Sie diese Deklaration zu special.h
hinzu:
Alternativ können Sie die Spezialisierung selbst in die Kopfzeile einfügen. Da eine Spezialisierung jedoch keine Vorlage mehr ist, folgt sie normalen Funktionsregeln und muss in einer Kopfzeile als inline
gekennzeichnet werden.
Seien Sie auch vorsichtig, dass Spezialisierungen für Funktionsvorlagen ein eher spezifisches Verhalten haben, und es ist im Allgemeinen besser, Überladungen als Spezialisierungen zu verwenden. Siehe Herb Sutters Artikel für Details.
Sie haben die eine Definitionsregel (ODR) verletzt. Also, was genau passiert? In main.cpp
ist keine Spezialisierung für special::call<string>
bekannt. Daher generiert der Compiler eine Instanziierung der Vorlage in die Übersetzungseinheit (TU), die "nicht so speziell" ausgibt. In special.cpp
wird eine vollständige Spezialisierung deklariert und definiert, sodass der Compiler diese Definition in die andere Übersetzungseinheit einfügt. Sie haben also zwei verschiedene Definitionen der gleichen Funktion in zwei verschiedenen Übersetzungseinheiten, was eine Verletzung des ODR darstellt, was bedeutet, dass es ein undefiniertes Verhalten ist.
Theoretisch kann das Ergebnis alles sein. Ein Compilerfehler, ein Absturz, eine stille Online-Bestellung für eine Pizza, alles . Sogar unterschiedliche Verhaltensweisen beim Debuggen und Freigeben kompilieren.
In der Praxis passiert Folgendes: Wenn Sie den Debug-Build verknüpfen, sieht der Linker dasselbe Symbol, das zweimal in den beiden TUs definiert ist. Dies ist nur für Templates und Inline-Funktionen zulässig. Wegen der ODR kann man davon ausgehen, dass beide Definitionen gleichwertig sind und die eine von special.cpp
auswählen, so dass Sie zufällig das Verhalten erhalten, das Sie erwarten.
Während der Erstellung des Releases iniert der Compiler den Aufruf von special::call<string>
während der Kompilierung von main.cpp
, so dass Sie das einzige in dieser TU gesehene Verhalten erhalten: "nicht so speziell".
Wie können Sie das beheben?
Um nur eine Definition für diese Spezialisierung zu haben, müssen Sie in einer TU definieren, wie Sie es getan haben, aber Sie müssen deklarieren , dass es eine vollständige Spezialisierung gibt andere TU, was bedeutet, dass die Spezialisierung in der Kopfzeile special.h
existiert:
Oder, was öfter zu sehen ist, definieren es in der Kopfzeile, so dass es in jeder TU angezeigt wird. Da vollständig spezialisierte Funktionsvorlagen normale Funktionen sind, müssen Sie sie inline definieren:
%Vor%