Dodam tylko szczegółowe odniesienie do reguły as-if i zmiennej niestabilnej słowa kluczowego . (Na dole tych stron postępuj zgodnie z instrukcjami „zobacz także” i „Referencje”, aby prześledzić oryginalne specyfikacje, ale wydaje mi się, że cppreference.com jest znacznie łatwiejszy do odczytania / zrozumienia).
W szczególności chcę, abyś przeczytał tę sekcję
obiekt ulotny - obiekt, którego typ jest nietrwały, lub podobiekt obiektu ulotnego lub zmienny podobiekt obiektu o stałej zmienności. Każdy dostęp (operacja odczytu lub zapisu, wywołanie funkcji składowej itp.) Dokonany za pomocą wyrażenia glvalue typu volatile-qualified jest traktowany jako widoczny efekt uboczny na potrzeby optymalizacji (to znaczy w ramach pojedynczego wątku wykonania, volatile Dostępów nie można zoptymalizować lub zmienić ich kolejności z innym widocznym efektem ubocznym, który jest sekwencjonowany przed lub sekwencjonowany po dostępie ulotnym. To sprawia, że obiekty ulotne nadają się do komunikacji z programem obsługi sygnału, ale nie z innym wątkiem wykonania, zobacz std :: memory_order ). Każda próba odniesienia się do obiektu ulotnego za pomocą nieulotnej wartości typu glvalue (np. Poprzez odniesienie lub wskaźnik do typu nieulotnego) skutkuje niezdefiniowanym zachowaniem.
Więc zmienne słowo kluczowe szczególności wyłączania optymalizacji kompilatora na glvalues . Jedyną rzeczą, na którą słowo kluczowe volatile może mieć wpływ, jest prawdopodobnie to return x
, że kompilator może zrobić, co zechce, z resztą funkcji.
To, jak bardzo kompilator może zoptymalizować zwrot, zależy od tego, jak bardzo kompilator może zoptymalizować dostęp x w tym przypadku (ponieważ nie zmienia kolejności, a ściśle mówiąc, nie usuwa wyrażenia zwrotnego. Jest dostęp , ale to odczytuje i zapisuje na stosie, co powinno być w stanie usprawnić.) Więc kiedy to czytam, jest to szary obszar określający, ile kompilator może optymalizować, i można go łatwo spierać w obie strony.
Uwaga dodatkowa: w takich przypadkach zawsze zakładaj, że kompilator zrobi odwrotnie niż chciałeś / potrzebowałeś. Powinieneś albo wyłączyć optymalizację (przynajmniej dla tego modułu), albo spróbować znaleźć bardziej zdefiniowane zachowanie dla tego, co chcesz. (To jest również powód, dla którego testowanie jednostkowe jest tak ważne) Jeśli uważasz, że to wada, powinieneś porozmawiać o tym z twórcami C ++.
To wszystko jest nadal bardzo trudne do odczytania, więc spróbuj uwzględnić to, co uważam za istotne, abyś mógł przeczytać to sam.
glvalue Wyrażenie glvalue to lwartość lub xwartość.
Nieruchomości:
Glvalue może być niejawnie konwertowane na prvalue z niejawną konwersją l-wartość-na-r-wartość, tablica-wskaźnik lub funkcja-wskaźnik. Wartość gl może być polimorficzna: dynamiczny typ obiektu, który identyfikuje, niekoniecznie jest statycznym typem wyrażenia. Wartość glvalue może mieć niekompletny typ, jeśli zezwala na to wyrażenie.
xvalue Następujące wyrażenia są wyrażeniami xvalue:
wywołanie funkcji lub przeciążone wyrażenie operatora, którego zwracanym typem jest odwołanie do obiektu rvalue, na przykład std :: move (x); a [n], wbudowane wyrażenie w indeksie dolnym, gdzie jeden operand jest tablicą rwartość; am, element członkowski wyrażenia obiektowego, gdzie a jest wartością r, a m jest niestatycznym składnikiem danych typu niereferencyjnego; a. * mp, wskaźnik do elementu członkowskiego wyrażenia obiektu, gdzie a jest wartością r, a mp jest wskaźnikiem do elementu danych; a? b: c, trójskładnikowe wyrażenie warunkowe dla niektórych b i c (szczegóły w definicji); wyrażenie rzutowania na rvalue odniesienie do typu obiektu, takie jak static_cast (x); każde wyrażenie, które oznacza tymczasowy obiekt po tymczasowej materializacji. (od C ++ 17) Właściwości:
To samo co rvalue (poniżej). To samo co wartość kleju (poniżej). W szczególności, podobnie jak wszystkie wartości r, wartości x wiążą się z odwołaniami do wartości r i podobnie jak wszystkie wartości gl, wartości x mogą być polimorficzne, a wartości x niebędące klasami mogą być kwalifikowane jako cv.
lwartość Następujące wyrażenia są wyrażeniami lwartości:
nazwa zmiennej, funkcji lub elementu członkowskiego danych, niezależnie od typu, na przykład std :: cin lub std :: endl. Nawet jeśli typ zmiennej to odwołanie do wartości r, wyrażenie składające się z jej nazwy jest wyrażeniem l-wartości; wywołanie funkcji lub wyrażenie przeciążonego operatora, którego zwracanym typem jest odwołanie do lvalue, na przykład std :: getline (std :: cin, str), std :: cout << 1, str1 = str2 lub ++ it; a = b, a + = b, a% = b oraz wszystkie inne wbudowane i złożone wyrażenia przypisania; ++ a i --a, wbudowane wyrażenia preinkrementacji i dekrementacji; * p, wbudowane wyrażenie pośrednie; a [n] i p [n], wbudowane wyrażenia z indeksem dolnym, z wyjątkiem sytuacji, gdy a jest tablicą rvalue (od C ++ 11); am, element członkowski wyrażenia obiektu, z wyjątkiem sytuacji, gdy m jest elementem wyliczającym składowym lub niestatyczną funkcją składową, lub gdzie a jest wartością r, a m jest niestatycznym składnikiem danych typu niereferencyjnego; p-> m, wbudowany element członkowski wyrażenia wskaźnika, z wyjątkiem sytuacji, gdy m jest elementem wyliczającym składowym lub niestatyczną funkcją składową; a. * mp, wskaźnik do elementu członkowskiego wyrażenia obiektu, gdzie a jest lwartością, a mp jest wskaźnikiem do elementu danych; p -> * mp, wbudowany wskaźnik do elementu członkowskiego wyrażenia wskaźnika, gdzie mp jest wskaźnikiem do elementu członkowskiego danych; a, b, wbudowane wyrażenie z przecinkiem, gdzie b jest lwartością; a? b: c, trójskładnikowe wyrażenie warunkowe dla niektórych b i c (np. gdy oba są wartościami tego samego typu, ale szczegóły w definicji); literał ciągu, taki jak „Hello, world!”; wyrażenie rzutowania na typ referencyjny lvalue, taki jak static_cast (x); wywołanie funkcji lub przeciążone wyrażenie operatora, którego zwracanym typem jest rvalue odwołanie do funkcji; wyrażenie rzutowania na rvalue odniesienie do typu funkcji, takie jak static_cast (x). (od C ++ 11) Właściwości:
To samo co wartość kleju (poniżej). Można przyjąć adres lwartości: & ++ i 1
i & std :: endl są poprawnymi wyrażeniami. Modyfikowalna lwartość może być użyta jako lewostronny operand wbudowanego przypisania i złożonych operatorów przypisania. Do zainicjowania odwołania do lwartości można użyć lwartości; to wiąże nową nazwę z obiektem zidentyfikowanym przez wyrażenie.
zasada jak gdyby
Kompilator C ++ może wprowadzać zmiany w programie, o ile spełnione są następujące warunki:
1) W każdym punkcie sekwencji wartości wszystkich obiektów ulotnych są stabilne (poprzednie oceny są zakończone, nowe oceny nie zostały rozpoczęte) (do C ++ 11) 1) Dostęp (odczyt i zapis) do obiektów ulotnych zachodzi ściśle zgodnie z semantyką wyrażeń, w których występują. W szczególności nie są one zmieniane w kolejności w odniesieniu do innych nietrwałych dostępów w tym samym wątku. (od C ++ 11) 2) W momencie zakończenia programu dane zapisywane do plików są dokładnie takie, jakby program był wykonywany tak, jak został zapisany. 3) Tekst podpowiedzi, który jest wysyłany do urządzeń interaktywnych, zostanie wyświetlony, zanim program zaczeka na wprowadzenie. 4) Jeśli ISO C pragma #pragma STDC FENV_ACCESS jest obsługiwana i ustawiona na ON,
Jeśli chcesz przeczytać specyfikacje, uważam, że to są te, które musisz przeczytać
Bibliografia
Norma C11 (ISO / IEC 9899: 2011): 6.7.3 Kwalifikatory typu (p: 121-123)
Norma C99 (ISO / IEC 9899: 1999): 6.7.3 Kwalifikatory typu (p: 108-110)
Norma C89 / C90 (ISO / IEC 9899: 1990): 3.5.3 Kwalifikatory typu