Aktualisiert, siehe unten!
Ich habe gehört und gelesen, dass C ++ 0x einem Compiler erlaubt, "Hallo" für das folgende Snippet zu drucken
#include <iostream>
int main() {
while(1)
;
std::cout << "Hello" << std::endl;
}
Es hat anscheinend etwas mit Threads und Optimierungsmöglichkeiten zu tun. Es scheint mir, dass dies viele Menschen überraschen kann.
Hat jemand eine gute Erklärung, warum dies notwendig war, um zuzulassen? Als Referenz heißt es im neuesten C ++ 0x-Entwurf unter6.5/5
Eine Schleife, die außerhalb der for-init-Anweisung im Fall einer for-Anweisung
- ruft die E / A-Funktionen der Bibliothek nicht auf und
- greift nicht auf flüchtige Objekte zu oder ändert sie nicht
- führt keine Synchronisationsoperationen (1.10) oder atomaren Operationen (Abschnitt 29) durch
kann von der Implementierung als beendet angenommen werden. [Hinweis: Dies soll Compiler-Transformationen ermöglichen, z. B. das Entfernen leerer Schleifen, auch wenn die Beendigung nicht nachgewiesen werden kann. - Endnote]
Bearbeiten:
Dieser aufschlussreiche Artikel sagt über diesen Standardtext
Leider werden die Wörter "undefiniertes Verhalten" nicht verwendet. Immer wenn der Standard sagt "der Compiler kann P annehmen", wird impliziert, dass ein Programm mit der Eigenschaft nicht-P eine undefinierte Semantik hat.
Ist das richtig und darf der Compiler "Bye" für das obige Programm drucken?
Es gibt hier einen noch aufschlussreicheren Thread , der sich mit einer analogen Änderung von C befasst, die von dem Kerl gestartet wurde, der den oben verlinkten Artikel geschrieben hat. Neben anderen nützlichen Fakten stellen sie eine Lösung vor, die auch für C ++ 0x zu gelten scheint ( Update : Dies funktioniert mit n3225 nicht mehr - siehe unten!)
endless:
goto endless;
Ein Compiler darf das anscheinend nicht optimieren, weil es keine Schleife ist, sondern ein Sprung. Ein anderer Mann fasst die vorgeschlagene Änderung in C ++ 0x und C201X zusammen
Durch das Schreiben einer Schleife behauptet der Programmierer entweder, dass die Schleife etwas mit sichtbarem Verhalten tut (E / A ausführt, auf flüchtige Objekte zugreift oder Synchronisations- oder Atomoperationen durchführt) oder dass sie schließlich beendet wird. Wenn ich gegen diese Annahme verstoße, indem ich eine Endlosschleife ohne Nebenwirkungen schreibe, lüge ich den Compiler an und das Verhalten meines Programms ist undefiniert. (Wenn ich Glück habe, warnt mich der Compiler möglicherweise davor.) Die Sprache bietet keine Möglichkeit, eine Endlosschleife ohne sichtbares Verhalten auszudrücken (nicht mehr?).
Update am 3.1.2011 mit n3225: Ausschuss hat den Text auf 1.10 / 24 verschoben und sagen
Die Implementierung kann davon ausgehen, dass ein Thread möglicherweise einen der folgenden Schritte ausführt:
- beenden,
- eine Bibliotheks-E / A-Funktion aufrufen,
- auf ein flüchtiges Objekt zugreifen oder es ändern, oder
- Führen Sie eine Synchronisationsoperation oder eine atomare Operation durch.
Der goto
Trick wird nicht mehr funktionieren!
int x = 1; for(int i = 0; i < 10; ++i) do_something(&i); x++;
in for(int i = 0; i < 10; ++i) do_something(&i); int x = 2;
? Oder möglicherweise umgekehrt, x
indem es 2
vor der Schleife initialisiert wird. Es kann erkennen, dass do_something
es sich nicht um den Wert von kümmert x
, daher ist es eine absolut sichere Optimierung, wenn do_something
sich der Wert von nicht i
so ändert, dass Sie in eine Endlosschleife geraten.
main() { start_daemon_thread(); while(1) { sleep(1000); } }
Programm möglicherweise sofort beendet wird, anstatt meinen Daemon in einem Hintergrundthread auszuführen?
while(1) { MyMysteriousFunction(); }
muss unabhängig kompilierbar sein, ohne die Definition dieser mysteriösen Funktion zu kennen, oder? Wie können wir also feststellen, ob Bibliotheks-E / A-Funktionen aufgerufen werden? Mit anderen Worten: Sicherlich ruft diese erste Kugel keine Funktion auf .