Zaktualizowano, patrz poniżej!
Słyszałem i przeczytałem, że C ++ 0x umożliwia kompilatorowi wydrukowanie „Hello” dla następującego fragmentu kodu
#include <iostream>
int main() {
while(1)
;
std::cout << "Hello" << std::endl;
}
Najwyraźniej ma to coś wspólnego z wątkami i możliwościami optymalizacji. Wydaje mi się jednak, że może to zaskoczyć wielu ludzi.
Czy ktoś ma dobre wyjaśnienie, dlaczego było to konieczne, aby zezwolić? Dla porównania, najnowsza wersja robocza C ++ 0x mówi o6.5/5
Pętla, która poza instrukcją for-init w przypadku instrukcji for,
- nie wywołuje funkcji biblioteki we / wy, a
- nie uzyskuje dostępu do obiektów ulotnych ani nie modyfikuje ich, oraz
- nie wykonuje operacji synchronizacji (1.10) ani operacji atomowych (klauzula 29)
może zostać uznane przez implementację za zakończone. [Uwaga: ma to na celu umożliwienie transformacji kompilatora, takich jak usuwanie pustych pętli, nawet jeśli nie można udowodnić zakończenia. - notatka końcowa]
Edytować:
Ten wnikliwy artykuł mówi o tym tekście Standardów
Niestety, słowa „niezdefiniowane zachowanie” nie są używane. Jednak za każdym razem, gdy standard mówi, że „kompilator może założyć P”, zakłada się, że program, który ma właściwość not-P, ma niezdefiniowaną semantykę.
Czy to prawda i czy kompilator może wydrukować „Do widzenia” dla powyższego programu?
Jest tu jeszcze bardziej wnikliwy wątek , który dotyczy analogicznej zmiany do C, zapoczątkowanej przez Guy'a wykonującego powyższy linkowany artykuł. Wśród innych przydatnych faktów przedstawiają rozwiązanie, które wydaje się mieć również zastosowanie do C ++ 0x ( Aktualizacja : To już nie zadziała z n3225 - patrz poniżej!)
endless:
goto endless;
Wydaje się, że kompilator nie może tego zoptymalizować, ponieważ nie jest to pętla, ale skok. Inny facet podsumowuje proponowaną zmianę w C ++ 0x i C201X
Pisząc pętlę, programista stwierdza , że albo pętla robi coś z widocznym zachowaniem (wykonuje operacje we / wy, uzyskuje dostęp do obiektów ulotnych, przeprowadza synchronizację lub operacje atomowe), albo że ostatecznie się kończy. Jeśli naruszę to założenie, pisząc nieskończoną pętlę bez skutków ubocznych, okłamuję kompilator, a zachowanie mojego programu jest nieokreślone. (Jeśli mam szczęście, kompilator może mnie o tym ostrzec.) Język nie zapewnia (już nie zapewnia?) Sposobu na wyrażenie nieskończonej pętli bez widocznego zachowania.
Aktualizacja z 3.1.2011 z n3225: Komisja przeniosła tekst do 1.10 / 24 i powiedz
Implementacja może zakładać, że dowolny wątek w końcu wykona jedną z następujących czynności:
- zakończyć,
- wywołanie funkcji wejścia / wyjścia biblioteki,
- uzyskać dostęp do zmiennego obiektu lub zmodyfikować go, lub
- wykonać operację synchronizacji lub operację atomową.
Ta goto
sztuczka już nie zadziała!
int x = 1; for(int i = 0; i < 10; ++i) do_something(&i); x++;
na for(int i = 0; i < 10; ++i) do_something(&i); int x = 2;
? Lub być może w drugą stronę, z x
inicjalizacją 2
przed pętlą. Potrafi stwierdzić, do_something
że nie dba o wartość x
, więc jest to całkowicie bezpieczna optymalizacja, jeśli do_something
nie powoduje zmiany wartości w i
taki sposób, że kończy się w nieskończonej pętli.
main() { start_daemon_thread(); while(1) { sleep(1000); } }
może natychmiast zakończyć działanie, zamiast uruchamiać demona w wątku w tle?
while(1) { MyMysteriousFunction(); }
muszą być niezależnie kompilowalne bez znajomości definicji tej tajemniczej funkcji, prawda? Jak więc możemy określić, czy wywołuje ona dowolne funkcje we / wy biblioteki? Innymi słowy: z pewnością ten pierwszy punktor mógłby zostać sformułowany w żaden sposób nie wywołuje funkcji .