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 ++x
een bijwerking is en x %= 10
een 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 = y
identiek is aan x = x OP y
maar met x
slechts éé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 x
gegarandeerd 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 ++x
gelijk aan x += 1
, wat ook wordt gedekt door het bovenstaande citaat: De toewijzing wordt gesequenced vóór de waardeberekening. Dus voor ++x
wordt de toename gesequenced vóór de waardeberekening.
Daarmee rekening houdend is het niet moeilijk om te zien dat in ++x %= 10
de 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:
++x
en10
worden geëvalueerd – waarvan de volgorde niet in volgorde staat, maar10
is slechts een letterlijke waarde, dus dat is hier niet relevant .++x
wordt geëvalueerd:- Eerst wordt de waarde van
x
verhoogd. - Vervolgens wordt de waardeberekening uitgevoerd en krijgen we een lwaarde die verwijst naar
x
.
- Eerst wordt de waarde van
- De opdracht is klaar. De bijgewerkte waarde van
x
wordt modulo10
genomen en toegewezen aanx
. - 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 optrue
als hetbool
is (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. Alsx
niet van het typebool
is, is de uitdrukking++x
gelijk aanx+=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 %= 10
op die waarde. Alleen het evalueren van de constante kan gelijktijdig zijn (wat geen kwaad kan), de rest is strikt gesequenced na al het andere.