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:
int main()
{
int count = 0 ;
int arrInt[2] = { count++, count++ } ;
return 0 ;
}
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 ):
warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
int arrInt[2] = { count++, count++ } ;
^ ~~
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:
int i = 0 ;
i = ++ i + 1;
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:
int i = 0 ;
++++i ;
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:
Der constexpr-Bezeichner erklärt, dass es möglich ist, den Wert der Funktion oder Variablen zur Kompilierungszeit auszuwerten. Solche Variablen und Funktionen können dann verwendet werden, wenn nur Ausdrücke für die Kompilierungszeitkonstante zulässig sind.
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:
#include <limits>
int main()
{
int x[std::numeric_limits<unsigned int>::min()+2] ;
}
Bei Verwendung clang
in C ++ 03 handelt es x
sich um ein Array mit variabler Länge, das eine Erweiterung darstellt und die folgende Warnung generiert:
warning: variable length arrays are a C99 feature [-Wvla-extension]
int x[std::numeric_limits<unsigned int>::min()+2] ;
^
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:
#include <iostream>
#include <stdexcept>
struct S
{
~S() { throw std::runtime_error(""); } // bad, but acceptable
};
int main()
{
try { S s; }
catch (...) {
std::cerr << "exception occurred";
}
std::cout << "success";
}
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:
#include <iostream>
#include <vector>
#include <algorithm>
class Comparators
{
public:
bool operator()(int first, int second)
{
return first < second;
}
};
int main()
{
class ComparatorsInner : public Comparators{};
std::vector<int> compares ;
compares.push_back(20) ;
compares.push_back(10) ;
compares.push_back(30) ;
ComparatorsInner comparatorInner;
std::sort(compares.begin(), compares.end(), comparatorInner);
std::vector<int>::iterator it;
for(it = compares.begin(); it != compares.end(); ++it)
{
std::cout << (*it) << std::endl;
}
}
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 :
#include <iostream>
template<int I> struct X {
static int const c = 2;
};
template<> struct X<0> {
typedef int c;
};
template<typename T> struct Y {
static int const c = 3;
};
static int const c = 4;
int main() {
std::cout << (Y<X<1> >::c >::c>::c) << '\n';
std::cout << (Y<X< 1>>::c >::c>::c) << '\n';
}
und das Ergebnis in C ++ 03 ist:
0
3
und in C ++ 11:
0
0
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 :
std::vector<T> test(1);
erzeugt unterschiedliche Ergebnisse in C ++ 03 und C ++ 11:
#include <iostream>
#include <vector>
struct T
{
bool flag;
T() : flag(false) {}
T(const T&) : flag(true) {}
};
int main()
{
std::vector<T> test(1);
bool is_cpp11 = !test[0].flag;
std::cout << is_cpp11 << std::endl ;
}
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:
int x[] = { 2.0 };
Dies wird im Entwurf des C ++ 11-Standardabschnitts, 8.5.4
Listeninitialisierung, Absatz 3, behandelt :
Die Listeninitialisierung eines Objekts oder einer Referenz vom Typ T ist wie folgt definiert:
und enthält die folgende Kugel ( Schwerpunkt Mine ):
Andernfalls werden Konstruktoren berücksichtigt, wenn T ein Klassentyp ist. Die anwendbaren Konstruktoren werden aufgelistet und der beste wird durch Überlastungsauflösung ausgewählt (13.3, 13.3.1.7). Wenn eine einschränkende Konvertierung (siehe unten) erforderlich ist, um eines der Argumente zu konvertieren, ist das Programm fehlerhaft
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
#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"
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.
#define _x "there"
"hello"_x // #1
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.
auto
dass dies zu einer