Welche Breaking Changes werden in C++11 eingeführt?

Lesezeit: 11 Minuten

Ich weiß, dass mindestens eine der Änderungen in C++11 dazu führt, dass alter Code nicht mehr kompiliert werden kann: die Einführung von explicit operator bool() in der Standardbibliothek und ersetzt alte Instanzen von operator void*(). Zugegeben, der Code, den dies brechen wird, ist wahrscheinlich Code, der von vornherein nicht hätte gültig sein sollen, aber es ist trotzdem eine Breaking Change: Programme, die früher gültig waren, sind es nicht mehr.

Gibt es weitere wichtige Änderungen?

  • Entfernen der Bedeutung von export Stichwort? Ich hole mir einen Mantel.

    – Steve Jessop

    19. Juni 2011 um 0:06 Uhr


  • Weißt du, ich würde es nicht als “brechende Änderung” bezeichnen … eher als “bestrafende Änderung”.

    – Xeo

    19. Juni 2011 um 0:07 Uhr

  • Wenn der ganze Papierkram, der für die Gründung einer solchen Gewerkschaft erforderlich ist, nur darauf wartet, abgesegnet zu werden, klar, warum nicht?

    – Dennis Zickefoose

    19. Juni 2011 um 0:07 Uhr

  • @Xeo: mystream.good() ist nicht dasselbe wie bool(mystream)? good() ist wahr, wenn kein Flag gesetzt ist. bool(mystream) ist immer noch falsch, wenn nur eofbit eingestellt ist. !mystream.fail() wäre das richtige Äquivalent.

    – R.Martinho Fernandes

    19. Juni 2011 um 0:32 Uhr


  • Anmerkung des Moderators: “Bitte halten Sie Kommentare zum Thema mit der Frage oder Antwort bereit. Wenn eine Frage oder Antwort diskutiert wird, sollte sich die Diskussion genau um diese Frage oder Antwort drehen. Diskussionen sind im Allgemeinen für Stack Overflow nicht konstruktiv. Antagonisieren ist sicherlich nicht.

    – Tim Post

    19. Juni 2011 um 15:11 Uhr


Das FDIS hat im Anhang einen Abschnitt für Inkompatibilitäten C.2 “C++ und ISO C++ 2003”.

Zusammenfassung, die FDIS hier umformuliert, um sie (besser) als SO-Antwort geeignet zu machen. Ich habe einige eigene Beispiele hinzugefügt, um die Unterschiede zu veranschaulichen.

Es gibt ein paar bibliotheksbezogene Inkompatibilitäten, deren Auswirkungen ich nicht genau kenne, daher überlasse ich es anderen, darauf näher einzugehen.

Kernsprache


#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"

#define _x "there"
"hello"_x // now a user-defined-string-literal. Previously, expanded _x .

Neue Schlüsselwörter: alignas, alignof, char16_t, char32_t, constexpr, decltype, noexcept, nullptr, static_assert und thread_local


Bestimmte Integer-Literale, die größer sind als durch long dargestellt werden können, könnten sich von einem Integer-Typ ohne Vorzeichen zu Long Long mit Vorzeichen ändern.


Gültiger C++ 2003-Code, der eine ganzzahlige Division verwendet, rundet das Ergebnis gegen 0 oder gegen minus unendlich, während C++0x das Ergebnis immer gegen 0 rundet.

(zugegebenermaßen kein wirkliches Kompatibilitätsproblem für die meisten Leute).


Gültiger C++ 2003-Code, der das Schlüsselwort verwendet auto als Speicherklassenbezeichner kann in C++0x ungültig sein.


Einengende Konvertierungen verursachen Inkompatibilitäten mit C++03. Beispielsweise ist der folgende Code in C++ 2003 gültig, aber in diesem Internationalen Standard ungültig, da double to int eine einschränkende Konvertierung ist:

int x[] = { 2.0 };

Implizit deklarierte spezielle Elementfunktionen werden als gelöscht definiert, wenn die implizite Definition falsch formatiert gewesen wäre.

Ein gültiges C++ 2003-Programm, das eine dieser speziellen Elementfunktionen in einem Kontext verwendet, in dem die Definition nicht erforderlich ist (z. B. in einem Ausdruck, der möglicherweise nicht ausgewertet wird), wird falsch formatiert.

Beispiel von mir:

struct A { private: A(); };
struct B : A { };
int main() { sizeof B(); /* valid in C++03, invalid in C++0x */ }

Solche Größentricks wurden von einigen SFINAE verwendet und müssen jetzt geändert werden 🙂


Vom Benutzer deklarierte Destruktoren haben eine implizite Ausnahmespezifikation.

Beispiel von mir:

struct A {
  ~A() { throw "foo"; }
};

int main() { try { A a; } catch(...) { } }

Dieser Code ruft terminate in C++0x, aber nicht in C++03. Da die implizite Ausnahmespezifikation von A::~A in C++0x ist noexcept(true).


Eine gültige C++ 2003-Deklaration mit export ist in C++0x falsch formatiert.


Ein gültiger C++ 2003-Ausdruck, der enthält > unmittelbar gefolgt von einem anderen > kann jetzt so behandelt werden, als würden zwei Vorlagen geschlossen.

In C++03, >> wäre immer das Shift-Operator-Token.


Abhängige Aufrufe von Funktionen mit interner Verknüpfung zulassen.

Beispiel von mir:

static void f(int) { }
void f(long) { }

template<typename T>
void g(T t) { f

int main() { g(0); }

In C++03 ruft dies auf f(long)aber in C++0x ruft dies auf f(int). Es sollte beachtet werden, dass sowohl in C++03 als auch in C++0x die folgenden Aufrufe f(B) (Der Instanziierungskontext berücksichtigt immer noch nur externe Verknüpfungsdeklarationen).

struct B { };
struct A : B { };

template<typename T>
void g(T t) { f

static void f(A) { }
void f(B) { }

int main() { A a; g(a); }

Je besser passend f(A) wird nicht übernommen, da keine externe Verlinkung vorhanden ist.


Änderungen in der Bibliothek

Gültiger C++ 2003-Code, der Bezeichner verwendet, die der C++-Standardbibliothek von C++0x hinzugefügt wurden, kann möglicherweise nicht kompiliert werden oder zu anderen Ergebnissen in dieser Internationalen Norm führen.


Gültiger C++ 2003-Code that #includes Header mit Namen von Headern der neuen C++0x-Standardbibliothek können in dieser Internationalen Norm ungültig sein.


Gültiger C++ 2003-Code, der kompiliert wurde und erwartet, dass sich Swap befindet <algorithm> müssen möglicherweise stattdessen enthalten <utility>


Der globale Namespace posix ist jetzt der Standardisierung vorbehalten.


Gültiger C++ 2003-Code, der definiert override, final, carries_dependencyoder noreturn als Makros ist in C++0x ungültig.

  • “Erlaube abhängige Aufrufe von Funktionen mit interner Verknüpfung.” Könnten Sie bitte den Unterschied zwischen Ihren beiden Beispielen erläutern? Mir fehlt eindeutig etwas.

    – Dennis Zickefoose

    19. Juni 2011 um 14:56 Uhr

  • @Dennis die Änderung wurde eingeführt von open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#561 . Obwohl sie die Tatsache nicht kommentieren, besteht der “Instanziierungskontext” immer noch nur aus “dem Satz von Deklarationen mit externer Verknüpfung, die vor dem Punkt der Instanziierung der Vorlagenspezialisierung in derselben Übersetzungseinheit deklariert wurden”. Die von ihnen vorgenommene Änderung wirkt sich also nur auf die Suche im Definitionskontext aus.

    – Johannes Schaub – litb

    19. Juni 2011 um 15:33 Uhr

  • In meinem ersten Beispiel war die interne Verknüpfungsfunktion sichtbar und im Definitionskontext der Vorlage zu finden. In meinem zweiten Beispiel müsste die interne Verknüpfungsfunktion Teil des Instanziierungskontexts sein, um gefunden zu werden. Aber da dies nicht der Fall ist, kann es nicht gefunden werden.

    – Johannes Schaub – litb

    19. Juni 2011 um 15:34 Uhr

  • Übrigens denke ich, dass der einzige Fall, in dem es für einen Vorlagendefinitionskontext sicher ist, eine Funktion mit interner Verknüpfung zu finden, darin besteht, wenn die Spezialisierung der Funktionsvorlage nur explizit in einer TU (wo die Vorlage definiert ist) instanziiert wird und alle anderen TUs sich darauf verlassen diese explizite Instantiierung. In allen anderen Fällen (in denen die anderen TUs die Spezialisierung selbst instanziieren würden) würden Sie gegen die ODR verstoßen, indem Sie die Vorlagendefinition jedes Mal eine andere (interne Verknüpfungs-)Funktion verwenden lassen.

    – Johannes Schaub – litb

    19. Juni 2011 um 15:39 Uhr

  • Ich bin mir also nicht sicher, warum sie die Beschränkung auf den Instanziierungskontext beibehalten haben – es würde nur eine (explizite) Instanziierung geben, und diese Instanziierung würde interne Verknüpfungsfunktionen verwenden, die im Instanziierungskontext der instanziierenden TU gefunden werden. Genauso wie es für den Definitionskontext der Fall wäre. Übrigens denke ich, wenn wir das noch hätten exportdann Ich denke die anderen TUs müssten sich nicht auf die explizite Instanziierung verlassen, sondern könnten die Vorlage selbst instanziieren. Dann es würde einen Unterschied machen, ob interne Verknüpfungsfunktionen im Instantiierungskontext sichtbar sind oder nicht.

    – Johannes Schaub – litb

    19. Juni 2011 um 15:43 Uhr

Die Bedeutung des Schlüsselworts auto hat sich geändert.

  • Wenn Sie die verwendet haben auto Schlüsselwort, irgendetwas stimmt mit Ihrem Code nicht. Warum um alles in der Welt würdest du es benutzen?

    – Elazar Leibowitsch

    19. Juni 2011 um 9:03 Uhr

  • Das ist kein brechende Veränderung. Jede gültige C++03-Verwendung von auto bleibt in C++11 gültig.

    – Drew Dormann

    1. Juli 2014 um 16:48 Uhr

  • @DrewDormann int main() { auto int i = 0; return i; } ist vollkommen gültiges C++03, aber ein Syntaxfehler in C++11. Die einzige Warnung, die Compiler im C++03-Modus dafür geben können, ist eine Warnung zur Kompatibilität.

    Benutzer743382

    14. September 2014 um 10:37 Uhr

Veränderung brechen?

Nun, zum einen, wenn Sie verwendet haben decltype, constexpr, nullptretc. als Identifikatoren, dann könnten Sie in Schwierigkeiten geraten …

Einige grundlegende Inkompatibilitäten, die nicht im Abschnitt „Inkompatibilitäten“ behandelt werden:


C++0x behandelt den eingefügten Klassennamen als Vorlage, wenn der Name als Argument an einen Vorlagenvorlagenparameter übergeben wird, und als Typ, wenn er an einen Vorlagentypparameter übergeben wird.

Gültiger C++03-Code kann sich anders verhalten, wenn er darauf angewiesen ist, dass der eingefügte Klassenname in diesen Szenarien immer ein Typ ist. Beispielcode entnommen aus meinem clang PR

template<template<typename> class X>
struct M { };

template<template<typename> class X>
void g(int = 0); // #1

template<typename T>
void g(long = 0); // #2

template<typename T>
struct A {
  void f() {
    g<A>(); /* is ambiguous in C++0x */
    g<A>(1); /* should choose #1 in C++0x */
  }
};

void h() {
  A<int> a;
  a.f();
}

In C++03 ruft der Code die zweite auf g beide Male.


C++0x macht einige Namen, die in C++03 abhängig waren, jetzt nicht-abhängig. Und erfordert, dass die Namenssuche nach nicht abhängigen qualifizierten Namen, die sich auf Mitglieder der aktuellen Klassenvorlage beziehen, bei der Instanziierung wiederholt wird, und erfordert die Überprüfung, dass diese Namen auf die gleiche Weise wie im Kontext der Vorlagendefinition gesucht werden.

Gültiger C++03-Code, der von der Dominanzregel abhängt, wird aufgrund dieser Änderung möglicherweise nicht mehr kompiliert.

Beispiel:

struct B { void f(); };

template<typename T>
struct A : virtual B { void f(); };

template<typename T>
struct C : virtual B, A<T> {
  void g() { this->f(); }
};

int main() { C<int> c; c.g(); }

Dieser gültige C++03-Code, der aufruft A<int>::f ist in C++0x nicht gültig, da die Namenssuche beim Instanziieren findet A<int>::f im Gegensatz zu B::fwodurch ein Konflikt mit der At-Definition-Suche verursacht wird.

Ob es sich hierbei um einen Defekt des FDIS handelt, ist derzeit noch nicht klar. Der Ausschuss ist sich dessen bewusst und wird die Situation bewerten.


Eine using-Deklaration, bei der der letzte Teil mit dem Bezeichner im letzten Teil des Qualifizierers im qualifizierten Namen identisch ist, der eine Basisklasse bezeichnet, diese using-Deklaration benennt nun den Konstruktor anstelle von Membern mit diesem Namen.

Beispiel:

struct A { protected: int B; };
typedef A B;

struct C : B {
  // inheriting constructor, instead of bringing A::B into scope
  using B::B;
};

int main() { C c; c.B = 0; }

Der obige Beispielcode ist wohlgeformt in C++03, aber schlecht geformt in C++0x, as A::B ist immer noch unzugänglich in main.

Stream-Extraktionsfehler werden anders behandelt.

Beispiel

#include <sstream>
#include <cassert>

int main()
{
   std::stringstream ss;
   ss << '!';
   
   int x = -1;
   
   assert(!(ss >> x)); // C++03 and C++11
   assert(x == -1);    // C++03
   assert(x == 0);     // C++11
}

Änderungsvorschlag

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3246.html#23

Standardreferenz

[C++03: 22.2.2.1.2/11]: Das Ergebnis der Verarbeitung der Stufe 2 kann eines von sein

  • In Stufe 2 hat sich eine Folge von Zeichen angesammelt, die umgewandelt wird (nach den Regeln von scanf) auf einen Wert vom Typ von val. Dieser Wert wird gespeichert in val und ios_base::goodbit darin gespeichert ist err.
  • Die in Stufe 2 akkumulierte Folge von Zeichen hätte verursacht scanf um einen Eingabefehler zu melden. ios_base::failbit zugeordnet ist err. [ed: Nothing is stored in val.]

[C++11: 22.4.2.1.2/3]: [..] Der zu speichernde numerische Wert kann einer der folgenden sein:

  • Null, wenn die Konvertierungsfunktion nicht das gesamte Feld konvertiert. ios_base::failbit zugeordnet ist err.
  • der positivste darstellbare Wert, wenn das Feld einen zu großen positiven Wert darstellt, um dargestellt zu werden val. ios_base::failbit zugeordnet ist err.
  • der negativste darstellbare Wert oder Null für einen vorzeichenlosen ganzzahligen Typ, wenn das Feld einen zu großen negativen Wert darstellt, um darin dargestellt zu werden val. ios_base::failbit zugeordnet ist err.
  • der konvertierte Wert, andernfalls.

Der resultierende numerische Wert wird in gespeichert val.

Implementierungen

  • AGB 4.8 korrekte Ausgaben für C++11:

    Assertion `x == -1′ ist fehlgeschlagen

  • GCC 4.5-4.8 alle Ausgaben für C++03 Folgendes, was ein Fehler zu sein scheint:

    Assertion `x == -1′ ist fehlgeschlagen

  • Visual C++ 2008 Express korrekte Ausgaben für C++03:

    Behauptung fehlgeschlagen: x == 0

  • Visual C++ 2012 Express gibt für C++11 falsch aus, was ein Problem mit dem Implementierungsstatus zu sein scheint:

    Behauptung fehlgeschlagen: x == 0

Inwiefern ist die Einführung expliziter Konvertierungsoperatoren eine bahnbrechende Änderung? Die alte Version bleibt genauso “gültig” wie zuvor.

Ja, die Änderung aus operator void*() const zu explicit operator bool() const wird eine bahnbrechende Änderung sein, aber nur, wenn sie auf eine Weise verwendet wird, die an und für sich falsch ist. Konformer Code wird nicht beschädigt.

Eine weitere bahnbrechende Änderung ist das Verbot von einschränkenden Konvertierungen während der Initialisierung von Aggregaten:

int a[] = { 1.0 }; // error

Bearbeiten: Nur zur Erinnerung, std::identity<T> wird in C++0x entfernt (siehe Hinweis). Es ist eine praktische Struktur, um Typen abhängig zu machen. Da die Struktur wirklich nicht viel tut, sollte dies das Problem beheben:

template<class T>
struct identity{
  typedef T type;
};

Es gibt zahlreiche Änderungen an der Containerbibliothek, die einen effizienteren Code ermöglichen, aber die Abwärtskompatibilität für einige Eckfälle stillschweigend unterbrechen.

Betrachten Sie zum Beispiel std::vectorStandardkonstruktion, C++0x und Breaking Changes.

988920cookie-checkWelche Breaking Changes werden in C++11 eingeführt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy