Ist es möglich, dass C ++ - Code sowohl dem C ++ 03- Standard als auch dem C ++ 11- Standard entspricht, aber je nachdem, unter welchem Standard er kompiliert wird, unterschiedliche Aktionen ausführt?
c++
c++11
language-lawyer
c++03
Erik Sjölund
quelle
quelle
auto
dass dies zu einer>>
Verwendung in einer Vorlage. Sie können eine Situation entwickeln, in der beide Standards kompiliert werden können. Eine andere, für die ich sicher leicht Änderungen finden würde, ist die Initialisierung.auto
dies verursacht werden kann. Mit der alten Bedeutungauto
erfordert eine Deklaration einen Typnamen. Mit der neuen Bedeutung ist ein Typname nicht zulässig.Antworten:
Die Antwort ist ein klares Ja. Auf der positiven Seite gibt es:
Auf der negativen Seite sind einige Beispiele im Anhang C der Norm aufgeführt. Obwohl es viel mehr negative als positive gibt, ist es viel weniger wahrscheinlich, dass jeder von ihnen auftritt.
String-Literale
und
Geben Sie Konvertierungen von 0 ein
In C ++ 11 sind nur Literale ganzzahlige Nullzeigerkonstanten:
Abgerundete Ergebnisse nach Integer Division und Modulo
In C ++ 03 durfte der Compiler entweder gegen 0 oder gegen negative Unendlichkeit runden. In C ++ 11 muss auf 0 gerundet werden
Leerzeichen zwischen verschachtelten Klammern zum Verschachteln von Vorlagen >> vs >>
Innerhalb einer Spezialisierung oder Instanziierung
>>
kann dies stattdessen als Rechtsverschiebung in C ++ 03 interpretiert werden. Es ist jedoch wahrscheinlicher, dass vorhandener Code beschädigt wird: (von http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )Der Bediener
new
kann jetzt andere Ausnahmen als auslösenstd::bad_alloc
Vom Benutzer deklarierte Destruktoren haben ein implizites Beispiel für eine Ausnahmespezifikation aus Welche wichtigen Änderungen werden in C ++ 11 eingeführt?
size()
von Containern ist jetzt erforderlich, um in O (1) zu laufenstd::ios_base::failure
leitet sich nichtstd::exception
mehr direkt von abDie direkte Basisklasse ist zwar neu,
std::runtime_error
aber nicht. So:quelle
noexecpt(true)
so ist, dassthrow
ein Destruktor jetzt aufruftstd::terminate
. Aber ich hoffe, jeder, der einen solchen Code geschrieben hat, wird sich darüber freuen!catch (std::exception &)
fängt also immer nochstd::ios_base::failure
.operator new
genau ist (es kann jetzt werfenstd::bad_array_new_length
), aber Ihr Beispiel zeigt das überhaupt nicht. Der angezeigte Code ist in C ++ 03 und C ++ 11 AFAIK identisch.Ich verweise Sie auf diesen Artikel und das Follow-up , das ein schönes Beispiel dafür enthält, wie
>>
die Bedeutung von C ++ 03 in C ++ 11 geändert werden kann, während in beiden noch kompiliert wird.Der Schlüsselteil ist die Zeile in
main
, die ein Ausdruck ist.In C ++ 03:
In C ++ 11
Herzlichen Glückwunsch, zwei unterschiedliche Ergebnisse für den gleichen Ausdruck. Zugegeben, das C ++ 03 hat beim Testen ein Warnformular für Clang erstellt.
quelle
typename
für::two
in C ++ 03 - Versiontrue
oderfalse
für die verschiedenen Standards zu bewerten . Vielleicht könnten wir es als Funktionstest verwenden </ witz>warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
), aber immer noch ein schönes Beispiel dafür, wie der mehrdeutige::
Operator seine Bedeutung ändert (entweder in Bezug auf den globalen Bereich oder indem er den direkt davor stehenden Operator dereferenziert)Ja, es gibt eine Reihe von Änderungen, die dazu führen, dass derselbe Code zu einem unterschiedlichen Verhalten zwischen C ++ 03 und C ++ 11 führt. Die Unterschiede bei den Sequenzierungsregeln führen zu einigen interessanten Änderungen, einschließlich eines zuvor nicht definierten Verhaltens, das genau definiert wird.
1. Mehrere Mutationen derselben Variablen innerhalb einer Initialisiererliste
Ein sehr interessanter Eckfall wären mehrere Mutationen derselben Variablen innerhalb einer Initialisiererliste, zum Beispiel:
Sowohl in C ++ 03 als auch in C ++ 11 ist dies gut definiert, aber die Reihenfolge der Auswertung in C ++ 03 ist nicht spezifiziert, aber in C ++ 11 werden sie in der Reihenfolge ausgewertet, in der sie erscheinen . Wenn wir also
clang
im C ++ 03-Modus kompilieren , wird die folgende Warnung ausgegeben ( siehe live ):In C ++ 11 wird jedoch keine Warnung angezeigt ( siehe live ).
2. Neue Sequenzierungsregeln machen i = ++ i + 1; gut definiert in C ++ 11
Die neuen Sequenzierungsregeln, die nach C ++ 03 übernommen wurden, bedeuten:
ist in C ++ 11 kein undefiniertes Verhalten mehr, dies wird im Fehlerbericht 637 behandelt. Sequenzierungsregeln und Beispiel stimmen nicht überein
3. Neue Sequenzierungsregeln machen auch ++++ i; gut definiert in C ++ 11
Die neuen Sequenzierungsregeln, die nach C ++ 03 übernommen wurden, bedeuten:
ist in C ++ 11 kein undefiniertes Verhalten mehr.
4. Etwas vernünftiger signierte Linksverschiebungen
Spätere Entwürfe von C ++ 11,
N3485
die ich unten verlinke, haben das undefinierte Verhalten beim Verschieben eines 1-Bits in das Vorzeichenbit oder darüber hinaus behoben . Dies wird auch im Fehlerbericht 1457 behandelt . Howard Hinnant kommentierte die Bedeutung dieser Änderung im Thread auf Ist Linksverschiebung (<<) ein negatives ganzzahliges undefiniertes Verhalten in C ++ 11? .5. constexpr-Funktionen können in C ++ 11 als Ausdrücke für die Kompilierungszeitkonstante behandelt werden
C ++ 11 führte constexpr- Funktionen ein, die:
Während C ++ 03 nicht über die Funktion constexpr verfügt, müssen wir das Schlüsselwort constexpr nicht explizit verwenden, da die Standardbibliothek in C ++ 11 viele Funktionen als constexpr bereitstellt . Zum Beispiel std :: numeric_limits :: min . Was zu unterschiedlichem Verhalten führen kann, zum Beispiel:
Bei Verwendung
clang
in C ++ 03 handelt esx
sich um ein Array mit variabler Länge, das eine Erweiterung darstellt und die folgende Warnung generiert:In C ++ 11
std::numeric_limits<unsigned int>::min()+2
handelt es sich um einen Ausdruck für die Kompilierungszeitkonstante, für den die VLA-Erweiterung nicht erforderlich ist.6. In C ++ 11 werden implizit keine Ausnahmespezifikationen für Ihre Destruktoren generiert
Da in C ++ 11 der benutzerdefinierte Destruktor eine implizite
noexcept(true)
Spezifikation hat, wie in noexcept destructors erläutert , bedeutet dies, dass das folgende Programm:In C ++ 11 wird aufgerufen,
std::terminate
aber in C ++ 03 erfolgreich ausgeführt.7. In C ++ 03 konnten Vorlagenargumente keine interne Verknüpfung haben
Dies wird ausführlich in Warum std :: sort keine in einer Funktion deklarierten Vergleichsklassen behandelt . Der folgende Code sollte also in C ++ 03 nicht funktionieren:
Derzeit
clang
ist dieser Code jedoch im C ++ 03-Modus mit einer Warnung-pedantic-errors
zulässig, es sei denn, Sie verwenden ein Flag, das irgendwie icky ist. Sehen Sie ihn live .8. >> ist beim Schließen mehrerer Vorlagen nicht mehr schlecht geformt
Die Verwendung
>>
zum Schließen mehrerer Vorlagen ist nicht mehr fehlerhaft, kann jedoch zu Code mit unterschiedlichen Ergebnissen in C ++ 03 und C + 11 führen. Das folgende Beispiel stammt aus rechtwinkligen Klammern und der Abwärtskompatibilität :und das Ergebnis in C ++ 03 ist:
und in C ++ 11:
9. C ++ 11 ändert einige der std :: vector-Konstruktoren
Leicht modifizierter Code aus dieser Antwort zeigt, dass der folgende Konstruktor von std :: vector verwendet wird :
erzeugt unterschiedliche Ergebnisse in C ++ 03 und C ++ 11:
10. Eingrenzen der Konvertierungen in aggregierten Initialisierern
In C ++ 11 ist eine sich verengende Konvertierung in Aggregatinitialisierern fehlerhaft und es sieht so aus, als ob
gcc
dies sowohl in C ++ 11 als auch in C ++ 03 möglich ist, obwohl in C ++ 11 standardmäßig eine Warnung angezeigt wird:Dies wird im Entwurf des C ++ 11-Standardabschnitts,
8.5.4
Listeninitialisierung, Absatz 3, behandelt :und enthält die folgende Kugel ( Schwerpunkt Mine ):
Dieses und viele mehr Instanz in der überdachten Entwurf C ++ Standard Abschnitt
annex C.2
C ++ und ISO C ++ 2003 . Es enthält auch:Neue Arten von Zeichenfolgenliteralen [...] Insbesondere Makros mit den Namen R, u8, u8R, u, uR, U, UR oder LR werden nicht erweitert, wenn sie an ein Zeichenfolgenliteral angrenzen, sondern als Teil des Zeichenfolgenliteral interpretiert . Beispielsweise
Benutzerdefinierte Unterstützung für Literalzeichenfolgen [...] Bisher bestand # 1 aus zwei separaten Vorverarbeitungstoken, und das Makro _x wurde erweitert. In diesem internationalen Standard besteht # 1 aus einem einzelnen Vorverarbeitungstoken, sodass das Makro nicht erweitert wird.
Geben Sie eine Rundung für die Ergebnisse des Integer / und% [...] 2003-Codes an, der eine Ganzzahldivision verwendet, um das Ergebnis gegen 0 oder gegen negative Unendlichkeit zu runden, während dieser Internationale Standard das Ergebnis immer gegen 0 rundet.
Die Komplexität der Mitgliedsfunktionen von size () ist jetzt konstant. [...] Einige Containerimplementierungen, die C ++ 2003 entsprechen, entsprechen möglicherweise nicht den in dieser internationalen Norm angegebenen Anforderungen an size (). Das Anpassen von Containern wie std :: list an die strengeren Anforderungen erfordert möglicherweise inkompatible Änderungen.
Die Basisklasse von std :: ios_base :: fail ändern [...] std :: ios_base :: fail wird nicht mehr direkt von std :: exception abgeleitet, sondern wird jetzt von std :: system_error abgeleitet, von dem wiederum abgeleitet wird std :: runtime_error. Gültiger C ++ 2003-Code, der davon ausgeht, dass std :: ios_base :: fail direkt von std :: exception abgeleitet ist, wird in diesem internationalen Standard möglicherweise anders ausgeführt.
quelle
Eine potenziell gefährliche rückwärts inkompatible Änderung betrifft Konstruktoren von Sequenzcontainern, z. B.
std::vector
die Überladung, die die Anfangsgröße angibt. Während in C ++ 03 ein standardmäßig erstelltes Element kopiert wurde, wird in C ++ 11 jedes standardmäßig erstellt.Betrachten Sie dieses Beispiel (mit
boost::shared_ptr
C ++ 03):C ++ 03 Live-Beispiel
C ++ 11 Live-Beispiel
Der Grund dafür ist, dass C ++ 03 eine Überladung für "Größe und Prototypelement angeben" und "Nur Größe angeben" wie folgt angegeben hat (Allokatorargumente der Kürze halber weggelassen):
Dies wird immer
prototype
in die Containerzeiten kopiertsize
. Wenn es mit nur einem Argument aufgerufen wird, werden dahersize
Kopien eines standardmäßig erstellten Elements erstellt.In C ++ 11 wurde diese Konstruktorsignatur entfernt und durch diese beiden Überladungen ersetzt:
Der zweite funktioniert wie zuvor und erstellt
size
Kopien desprototype
Elements. Der erste (der jetzt Aufrufe nur mit dem angegebenen Größenargument verarbeitet) erstellt jedoch standardmäßig jedes Element einzeln.Meine Vermutung für den Grund dieser Änderung ist, dass die C ++ 03-Überladung mit einem Elementtyp, der nur verschoben werden kann, nicht verwendet werden kann. Aber es ist trotzdem eine bahnbrechende Veränderung, und eine, die selten dokumentiert wird.
quelle
deque
, dass zehn separate Widgets gespeichert werden und nicht zehn Widgets, die dieselbe Ressource gemeinsam nutzen.Das Ergebnis eines fehlgeschlagenen Lesevorgangs von
std::istream
hat sich geändert. CppReference fasst es gut zusammen:Dies ist in erster Linie ein Problem, wenn Sie an die neue Semantik gewöhnt sind und dann mit C ++ 03 schreiben müssen. Folgendes ist keine besonders gute Praxis, aber in C ++ 11 gut definiert:
In C ++ 03 verwendet der obige Code jedoch eine nicht initialisierte Variable und weist daher ein undefiniertes Verhalten auf.
quelle
int x = 1, y = 1; cin >> x >> y; cout << x*y;
. Mit C ++ 03 hätte dies korrekt erzeugt,x
wenn keiny
gelesen werden konnte.Dieser Thread Welche Unterschiede zwischen C ++ 03 und C ++ 0x, falls vorhanden, zur Laufzeit erkannt werden können, enthält Beispiele (von diesem Thread kopiert), um Sprachunterschiede zu ermitteln, z. B. durch Ausnutzen der reduzierten C ++ 11-Referenz:
und c ++ 11, das lokale Typen als Vorlagenparameter zulässt:
quelle
Hier ist ein weiteres Beispiel:
Drucke:
Sehen Sie das Ergebnis auf Coliru
quelle