Is ++x %= 10 goed gedefinieerd in C++?

Tijdens het doorbladeren van de code van een project kwam ik de volgende verklaring tegen:

++x %= 10;

Is deze verklaring goed gedefinieerd in C++ of valt deze in dezelfde categorie als

a[i] = i++

?


Antwoord 1, autoriteit 100%

Volgens C++11 1.9 Program execution /15:

Behalve waar anders aangegeven, zijn evaluaties van operanden van individuele operators en van subexpressies van individuele expressies ongesequenced.

Als een bijwerking op een scalair object niet op volgorde staat ten opzichte van een ander neveneffect op hetzelfde scalaire object of een waardeberekening waarbij de waarde van hetzelfde scalaire object wordt gebruikt, is het gedrag ongedefinieerd.

In dit geval geloof ik dat ++xeen bijwerking is en x %= 10een waardeberekening is, dus je zou denken dat het ongedefinieerd gedrag is. De sectie toewijzing(5.17 /1) heeft echter dit te zeggen (mijn vetgedrukt):

In alle gevallen wordt de toewijzing gerangschikt na de waardeberekening van de rechter- en linkeroperanden,en vóór de waardeberekening van de toewijzingsexpressie.

Dat betekent dat beide kanten worden gesequenced vóór de opdracht en voordat het resultaat van de opdracht beschikbaar wordt gesteld. En aangezien de standaard ook stelt (5.17 /7) dat x OP = yidentiek is aan x = x OP ymaar met xslechts één keer wordt geëvalueerd, blijkt dit isgoed gedefinieerd gedrag, omdat het equivalent is aan:

++x = Q % 10; // where Q is the value from ++x, not evaluated again.

De enige vraag die dan overblijft is welke kantvan de opdracht wordt geëvalueerd, aangezien zeniet in volgorde staan. Ik denk echter dat het in dit geval niet uitmaakt, aangezien beide hetzelfde effect hebben:

++x = Q % 10; // LHS evaluated first
Q = ++x % 10; // RHS evaluated first

Dat is mijnlezing van de standaard. Hoewel ik behoorlijk wat ervaring heb met het decoderen van complexe documenten, is er misschieniets dat ik heb gemist – ik denk het niet, want we hebben hier allemaal een levendige discussie gehad om op dit punt te komen 🙂 en ik denk dat we allemaal de relevante secties hebben vastgesteld.

Maar of het nu goed gedefinieerd is of niet, fatsoenlijke coders zou nietop die manier code moeten schrijven. Het is lang geleden sinds de dagen van weinig geheugen/kleine opslagruimte van de PDP-mini’s, het werd tijd dat we onze code schreven om leesbaar te zijn.

Als je wilt verhogen, neem dan de modulo, gebruik x = (x + 1) % 10, al was het maar om het voor de volgende arme Joe die die code leest gemakkelijker te begrijpen te maken.

p>


Antwoord 2, autoriteit 34%

TL;DR: het is goed gedefinieerd omdat xgegarandeerd wordt verhoogd vóór de opdracht.


Sommige C++11 standaard

[intro.execution]/15:

Behalve waar aangegeven, evaluaties van operanden van individuele operators
en van subexpressies van individuele expressies zijn ongesequenced.

Echter, [expr.ass]/1 merkt iets op:

In alle gevallen wordt de toewijzing gesequenced na de waardeberekening
van de rechter en linker operanden
, en vóór de waardeberekening van
de opdrachtuitdrukking.

Dit vormt dus wel een uitzondering op het eerste citaat. Bovendien, zoals vermeld in [expr.pre.incr]1, is ++xgelijk aan x += 1, wat ook wordt gedekt door het bovenstaande citaat: De toewijzing wordt gesequenced vóór de waardeberekening. Dus voor ++xwordt de toename gesequenced vóór de waardeberekening.

Daarmee rekening houdend is het niet moeilijk om te zien dat in ++x %= 10de verhoging wordt gedaan voordat de opdracht is.
Dus het increment-neveneffect wordt gesequenced vóór het toewijzings-neveneffect, en dus worden alle betrokken neveneffecten gesequenced.

Om het anders te zeggen, de norm legt de volgende volgorde op:

  • ++xen 10worden geëvalueerd – waarvan de volgorde niet in volgorde staat, maar 10is slechts een letterlijke waarde, dus dat is hier niet relevant .
    • ++xwordt geëvalueerd:
      • Eerst wordt de waarde van xverhoogd.
      • Vervolgens wordt de waardeberekening uitgevoerd en krijgen we een lwaarde die verwijst naar x.
  • De opdracht is klaar. De bijgewerkte waarde van xwordt modulo 10genomen en toegewezen aan x.
  • De waardeberekening van de opdracht kan volgen, die duidelijk wordt gesequenced na de opdracht.

Vandaar

Als een bijwerking op een scalair object geen volgorde heeft ten opzichte van een van beide
een ander neveneffect op hetzelfde scalaire object of een waardeberekening
als de waarde van hetzelfde scalaire object wordt gebruikt, is het gedrag niet gedefinieerd.

is niet van toepassing omdat de neveneffecten en waardeberekeningen worden gesequenced.


1Niet [expr.post.incr] wat voor de postfix-verhoging zou zijn!


Antwoord 3, autoriteit 31%

Laten we eens kijken naar de unaire increment-operator:

5.3.2 Verhogen en verlagen [expr.pre.incr]

1 De operand van het voorvoegsel ++wordt gewijzigd door 1 toe te voegen of ingesteld op trueals het boolis (dit gebruik is verouderd ). De operand moet een aanpasbare waarde zijn. Het type operand moet een rekenkundig type zijn of een verwijzing naar een volledig gedefinieerd objecttype. Het resultaat is de bijgewerkte operand; het is een lwaarde, en het is een bitveld als de operand een bitveld is. Als xniet van het type boolis, is de uitdrukking ++xgelijk aan x+=1.

[…]

Dus alle evaluaties en bijwerkingen met betrekking tot die unaire operator worden gepland vóór zijn waarde en kunnen dus geen schade aanrichten.

Het enige dat overblijft is het evalueren van %= 10op die waarde. Alleen het evalueren van de constante kan gelijktijdig zijn (wat geen kwaad kan), de rest is strikt gesequenced na al het andere.

Other episodes