Angenommen, ich habe drei kompilierte Objekte, die alle von demselben Compiler / derselben Version erstellt wurden :
- A wurde mit dem C ++ 11-Standard kompiliert
- B wurde mit dem C ++ 14-Standard kompiliert
- C wurde mit dem C ++ 17-Standard kompiliert
Nehmen wir der Einfachheit halber an, dass alle Header in C ++ 11 geschrieben wurden und nur Konstrukte verwendet wurden, deren Semantik sich zwischen allen drei Standardversionen nicht geändert hat. Daher wurden alle Abhängigkeiten mit der Einbeziehung von Headern korrekt ausgedrückt und der Compiler hatte keine Einwände.
Um welche Kombinationen dieser Objekte handelt es sich und ist es nicht sicher, eine einzige Binärdatei zu verknüpfen? Warum?
BEARBEITEN: Antworten zu wichtigen Compilern (z. B. gcc, clang, vs ++) sind willkommen
std::string
Implementierung in libstdc ++ ist unabhängig vom verwendeten-std
Modus . Dies ist eine wichtige Eigenschaft, genau zu Unterstützung Situationen wie die OPs. Sie können den neuenstd::string
Code in C ++ 03 und den altenstd::string
Code in C ++ 11 verwenden (siehe den Link in Matteos späterem Kommentar).Antworten:
Für GCC ist es sicher, eine beliebige Kombination von Objekten A, B und C miteinander zu verknüpfen. Wenn sie alle mit derselben Version erstellt wurden, sind sie ABI-kompatibel. Die Standardversion (dh die
-std
Option) macht keinen Unterschied.Warum? Denn das ist eine wichtige Eigenschaft unserer Implementierung, an deren Sicherstellung wir hart arbeiten.
Wenn Sie Probleme haben, verknüpfen Sie Objekte, die mit verschiedenen Versionen von GCC kompiliert wurden, und Sie haben instabile Funktionen aus einem neuen C ++ - Standard verwendet, bevor die Unterstützung von GCC für diesen Standard abgeschlossen ist. Wenn Sie beispielsweise ein Objekt mit GCC 4.9
-std=c++11
und ein anderes Objekt mit GCC 5 kompilieren, treten-std=c++11
Probleme auf. Die C ++ 11-Unterstützung war in GCC 4.x experimentell, und daher gab es inkompatible Änderungen zwischen den GCC 4.9- und 5-Versionen der C ++ 11-Funktionen. Wenn Sie ein Objekt mit GCC 7 und ein-std=c++17
anderes Objekt mit GCC 8 kompilieren und-std=c++17
Probleme haben, treten Probleme auf, da die C ++ 17-Unterstützung in GCC 7 und 8 noch experimentell ist und sich weiterentwickelt.Auf der anderen Seite funktioniert jede Kombination der folgenden Objekte (siehe Hinweis zur
libstdc++.so
Version unten ):-std=c++03
-std=c++11
-std=c++17
Dies liegt daran, dass die C ++ 03-Unterstützung in allen drei verwendeten Compilerversionen stabil ist und die C ++ 03-Komponenten daher zwischen allen Objekten kompatibel sind. Die C ++ 11-Unterstützung ist seit GCC 5 stabil, aber Objekt D verwendet keine C ++ 11-Funktionen, und die Objekte E und F verwenden beide Versionen, bei denen die C ++ 11-Unterstützung stabil ist. Die C ++ 17-Unterstützung ist in keiner der verwendeten Compilerversionen stabil, aber nur Objekt F verwendet C ++ 17-Funktionen. Daher gibt es keine Kompatibilitätsprobleme mit den beiden anderen Objekten (die einzigen Funktionen, die sie gemeinsam nutzen, stammen aus C ++ 03 oder C ++ 11, und die verwendeten Versionen machen diese Teile OK). Wenn Sie später ein viertes Objekt, G, mit GCC 8
-std=c++17
kompilieren möchten, müssen Sie F mit derselben Version neu kompilieren (oder nicht mit F verknüpfen), da die C ++ 17-Symbole in F und G nicht kompatibel sind.Die einzige Einschränkung für die oben beschriebene Kompatibilität zwischen D, E und F besteht darin, dass Ihr Programm die
libstdc++.so
gemeinsam genutzte Bibliothek von GCC 7 (oder höher) verwenden muss. Da Objekt F mit GCC 7 kompiliert wurde, müssen Sie die gemeinsam genutzte Bibliothek aus dieser Version verwenden, da beim Kompilieren eines Teils des Programms mit GCC 7 Abhängigkeiten von Symbolen entstehen können, die inlibstdc++.so
GCC 4.9 oder GCC 5 nicht vorhanden sind. Wenn Sie mit dem mit GCC 8 erstellten Objekt G verknüpft sind, müssen Sie daslibstdc++.so
von GCC 8 verwenden, um sicherzustellen, dass alle von G benötigten Symbole gefunden werden. Die einfache Regel besteht darin, sicherzustellen, dass die gemeinsam genutzte Bibliothek, die das Programm zur Laufzeit verwendet, mindestens so neu ist wie die Version, die zum Kompilieren eines der Objekte verwendet wird.Eine weitere Einschränkung bei der Verwendung von GCC, die bereits in den Kommentaren zu Ihrer Frage erwähnt wurde, ist, dass seit GCC 5 zwei Implementierungen von
std::string
in libstdc ++ verfügbar sind. Die beiden Implementierungen sind nicht verlinkungskompatibel (sie haben unterschiedliche verstümmelte Namen, können also nicht miteinander verknüpft werden), können jedoch in derselben Binärdatei nebeneinander existieren (sie haben unterschiedliche verstümmelte Namen, also keine Konflikte, wenn ein Objektstd::string
und das verwendet werden andere Verwendungenstd::__cxx11::string
). Wenn Ihre Objekte verwendet werdenstd::string
, sollten sie normalerweise alle mit derselben Zeichenfolgenimplementierung kompiliert werden. Kompilieren Sie mit-D_GLIBCXX_USE_CXX11_ABI=0
, um die ursprünglichegcc4-compatible
Implementierung oder-D_GLIBCXX_USE_CXX11_ABI=1
die neuecxx11
Implementierung auszuwählen (lassen Sie sich nicht vom Namen täuschen, es kann auch in C ++ 03 verwendet werden, es heißtcxx11
weil es den C ++ 11-Anforderungen entspricht). Welche Implementierung der Standard ist, hängt davon ab, wie GCC konfiguriert wurde. Der Standard kann jedoch beim Kompilieren mit dem Makro immer überschrieben werden.quelle
Die Antwort besteht aus zwei Teilen. Kompatibilität auf Compilerebene und Kompatibilität auf Linker-Ebene. Beginnen wir mit dem ersteren.
Die Verwendung desselben Compilers bedeutet, dass unabhängig vom C ++ - Zielstandard derselbe Standardbibliotheksheader und die gleichen Quelldateien (die dem Compiler zugeordneten Onces) verwendet werden. Daher werden die Header-Dateien der Standardbibliothek so geschrieben, dass sie mit allen vom Compiler unterstützten C ++ - Versionen kompatibel sind.
Wenn jedoch die zum Kompilieren einer Übersetzungseinheit verwendeten Compileroptionen einen bestimmten C ++ - Standard angeben, sollten auf alle Funktionen, die nur in neueren Standards verfügbar sind, nicht zugegriffen werden können. Dies geschieht mit der
__cplusplus
Direktive. In der Vektorquelldatei finden Sie ein interessantes Beispiel für die Verwendung. Ebenso lehnt der Compiler alle syntaktischen Funktionen ab, die neuere Versionen des Standards bieten.All dies bedeutet, dass Ihre Annahme nur für die von Ihnen geschriebenen Header-Dateien gelten kann. Diese Header-Dateien können Inkompatibilitäten verursachen, wenn sie in verschiedenen Übersetzungseinheiten enthalten sind, die auf unterschiedliche C ++ - Standards abzielen. Dies wird in Anhang C des C ++ - Standards erörtert. Es gibt 4 Klauseln, ich werde nur die erste diskutieren und den Rest kurz erwähnen.
C.3.1 Abschnitt 2: lexikalische Konventionen
Einfache Anführungszeichen begrenzen ein Zeichenliteral in C ++ 11, während sie in C ++ 14 und C ++ 17 Zifferntrennzeichen sind. Angenommen, Sie haben die folgende Makrodefinition in einer der reinen C ++ 11-Headerdateien:
Betrachten Sie zwei Übersetzungseinheiten, die die Header-Datei enthalten, jedoch auf C ++ 11 bzw. C ++ 14 abzielen. Bei der Ausrichtung auf C ++ 11 wird das Komma in Anführungszeichen nicht als Parametertrennzeichen betrachtet. Es gibt nur einen Parameter. Daher wäre der Code äquivalent zu:
Wenn Sie dagegen auf C ++ 14 abzielen, werden die einfachen Anführungszeichen als Zifferntrennzeichen interpretiert. Daher wäre der Code äquivalent zu:
Der Punkt hier ist, dass die Verwendung von einfachen Anführungszeichen in einer der reinen C ++ 11-Headerdateien zu überraschenden Fehlern in den Übersetzungseinheiten führen kann, die auf C ++ 14/17 abzielen. Selbst wenn eine Header-Datei in C ++ 11 geschrieben ist, muss sie daher sorgfältig geschrieben werden, um sicherzustellen, dass sie mit späteren Versionen des Standards kompatibel ist. Die
__cplusplus
Richtlinie kann hier nützlich sein.Die anderen drei Klauseln des Standards umfassen:
C.3.2 Abschnitt 3: Grundbegriffe
C.3.3 Abschnitt 7: Erklärungen
C.3.4 Abschnitt 27: Eingabe- / Ausgabebibliothek
Mögliche Inkompatibilitäten zwischen C ++ 14 und C ++ 17 werden in C.4 erläutert. Da alle nicht standardmäßigen Header-Dateien in C ++ 11 geschrieben sind (wie in der Frage angegeben), treten diese Probleme nicht auf, sodass ich sie hier nicht erwähnen werde.
Jetzt werde ich die Kompatibilität auf Linker-Ebene diskutieren. Mögliche Gründe für Inkompatibilitäten sind im Allgemeinen:
main
Einstiegspunkt.Wenn das Format der resultierenden Objektdatei vom C ++ - Zielstandard abhängt, muss der Linker in der Lage sein, die verschiedenen Objektdateien zu verknüpfen. In GCC, LLVM und VC ++ ist dies glücklicherweise nicht der Fall. Das heißt, das Format der Objektdateien ist unabhängig vom Zielstandard dasselbe, obwohl es stark vom Compiler selbst abhängt. Tatsächlich erfordert keiner der Linker von GCC, LLVM und VC ++ Kenntnisse über den Ziel-C ++ - Standard. Dies bedeutet auch, dass wir bereits kompilierte Objektdateien verknüpfen können (statische Verknüpfung der Laufzeit).
Wenn die Programmstartroutine (die aufrufende Funktion
main
) für verschiedene C ++ - Standards unterschiedlich ist und die verschiedenen Routinen nicht miteinander kompatibel sind, ist es nicht möglich, die Objektdateien zu verknüpfen. In GCC, LLVM und VC ++ ist dies glücklicherweise nicht der Fall. Darüber hinaus ist die Signatur dermain
Funktion (und die für sie geltenden Einschränkungen, siehe Abschnitt 3.6 des Standards) in allen C ++ - Standards gleich, sodass es keine Rolle spielt, in welcher Übersetzungseinheit sie vorhanden ist.Im Allgemeinen funktioniert WPO möglicherweise nicht gut mit Objektdateien, die mit unterschiedlichen C ++ - Standards kompiliert wurden. Dies hängt genau davon ab, welche Phasen des Compilers Kenntnisse des Zielstandards erfordern und welche nicht, und welche Auswirkungen dies auf prozedurale Optimierungen hat, die objektdateienübergreifend sind. Glücklicherweise sind GCC, LLVM und VC ++ gut gestaltet und haben dieses Problem nicht (das ist mir nicht bekannt).
Daher wurden GCC, LLVM und VC ++ entwickelt, um die Binärkompatibilität zwischen verschiedenen Versionen des C ++ - Standards zu ermöglichen. Dies ist jedoch nicht wirklich eine Anforderung des Standards selbst.
By the way, obwohl der VC ++ Compiler bietet die std Schalter , mit dem Sie eine bestimmte Version des C ++ Standard Ziel ermöglicht, unterstützt es nicht Targeting C ++ 11. Die Mindestversion, die angegeben werden kann, ist C ++ 14, die Standardeinstellung ab Visual C ++ 2013 Update 3. Sie könnten eine ältere Version von VC ++ verwenden, um auf C ++ 11 abzuzielen, müssten dann jedoch andere VC ++ - Compiler verwenden verschiedene Übersetzungseinheiten zu kompilieren, die auf verschiedene Versionen des C ++ - Standards abzielen, die zumindest WPO beschädigen würden.
CAVEAT: Meine Antwort ist möglicherweise nicht vollständig oder sehr präzise.
quelle
Neue C ++ - Standards bestehen aus zwei Teilen: Sprachfunktionen und Standardbibliothekskomponenten.
Wie Sie unter neuem Standard verstehen , gibt es Änderungen in der Sprache selbst (z. B. für bestimmte Bereiche) fast kein Problem (manchmal bestehen Konflikte in Bibliotheksheadern von Drittanbietern mit neueren Standardsprachenfunktionen).
Aber Standardbibliothek ...
Jede Compilerversion enthält eine Implementierung der C ++ - Standardbibliothek (libstdc ++ mit gcc, libc ++ mit clang, MS C ++ - Standardbibliothek mit VC ++, ...) und genau eine Implementierung, nicht viele Implementierungen für jede Standardversion. In einigen Fällen können Sie auch eine andere Implementierung der Standardbibliothek als den bereitgestellten Compiler verwenden. Was Sie beachten sollten, ist die Verknüpfung einer älteren Standardbibliotheksimplementierung mit einer neueren.
Der Konflikt, der zwischen Bibliotheken von Drittanbietern und Ihrem Code auftreten kann, ist die Standardbibliothek (und andere Bibliotheken), die mit diesen Bibliotheken von Drittanbietern verknüpft ist.
quelle