Was erlauben C++?
-
- Moderator
- Beiträge: 2149
- Registriert: 25.02.2009, 13:37
Was erlauben C++?
In einem anderen Forum postete jemand sinngemäß diesen code (minimal verändert)
int dx = (!direction && x > 20) ? -1
: (direction && x < 200) ? 1
: 0 & (direction = !direction);
Falls nicht offensichtlich, der "Trick" um den es geht ist wie man die Zuweisung in einen Zweig des ? Operator bekommt. Ihr müsst nicht diskutieren, warum das einen bescheuerte Idee ist, darüber waren sich sofort alle einig. Mich hat aber als langjähriger Mitleser hier eher mal interessiert ob das überhaupt so funktioniert, wie der Autor sich das vorstellt oder nicht einfach wegoptimiert wird.
Ich habe godbolt dann auch in der Tat dazu bekommen, mit und ohne die Zuweisung den gleichen Text zu erzeugen
https://godbolt.org/z/cfnYMaWE3
was jetzt naiv erstmal bedeutet, dass die Zuweisung nichts tut. Ich war dann aber im zweiten Nachdenken nicht mehr sicher ob das jetzt wirklich einfach 0 & ... zu 0 short-circuited oder ob der Compiler das in meinem Beispiel aus einem anderen Grund darf, oder ob er einfach einen Logik Trick gefunden hat, den ich nicht verstehe.
Weiß ich nicht wie lange der Link gültig bleibt. FWIW:
f(bool, int):
cmp esi, 20
setle al
or al, dil
je .L1
xor edi, 1
cmp esi, 199
setle al
or eax, edi
.L1:
ret
Also Fragen:
1) Ist die Zuweisung noch irgendwie im Ergebnis implizit und ich sehe sie nur nicht
oder
2) Hat der Compiler in der Tat die Zuweisung wegoptimiert [wie von mir erahnt] und wenn ja, warum durfte er das?
Bevor jemand meckert, natürlich habe ich c++ bitwise short circuit gegooglet, aber die Ergebnisse sind so furchtbar (Beispiel: https://stackoverflow.com/questions/968 ... rt-circuit) da frage ich lieber hier.
int dx = (!direction && x > 20) ? -1
: (direction && x < 200) ? 1
: 0 & (direction = !direction);
Falls nicht offensichtlich, der "Trick" um den es geht ist wie man die Zuweisung in einen Zweig des ? Operator bekommt. Ihr müsst nicht diskutieren, warum das einen bescheuerte Idee ist, darüber waren sich sofort alle einig. Mich hat aber als langjähriger Mitleser hier eher mal interessiert ob das überhaupt so funktioniert, wie der Autor sich das vorstellt oder nicht einfach wegoptimiert wird.
Ich habe godbolt dann auch in der Tat dazu bekommen, mit und ohne die Zuweisung den gleichen Text zu erzeugen
https://godbolt.org/z/cfnYMaWE3
was jetzt naiv erstmal bedeutet, dass die Zuweisung nichts tut. Ich war dann aber im zweiten Nachdenken nicht mehr sicher ob das jetzt wirklich einfach 0 & ... zu 0 short-circuited oder ob der Compiler das in meinem Beispiel aus einem anderen Grund darf, oder ob er einfach einen Logik Trick gefunden hat, den ich nicht verstehe.
Weiß ich nicht wie lange der Link gültig bleibt. FWIW:
f(bool, int):
cmp esi, 20
setle al
or al, dil
je .L1
xor edi, 1
cmp esi, 199
setle al
or eax, edi
.L1:
ret
Also Fragen:
1) Ist die Zuweisung noch irgendwie im Ergebnis implizit und ich sehe sie nur nicht
oder
2) Hat der Compiler in der Tat die Zuweisung wegoptimiert [wie von mir erahnt] und wenn ja, warum durfte er das?
Bevor jemand meckert, natürlich habe ich c++ bitwise short circuit gegooglet, aber die Ergebnisse sind so furchtbar (Beispiel: https://stackoverflow.com/questions/968 ... rt-circuit) da frage ich lieber hier.
Re: Was erlauben C++?
0 & irgendwas ist 0, also don'tcare.
Der C++-Compiler scheint nicht beachtet zu haben, dass die Zuweisung ja trotzdem einen Seiteneffekt hat, der in jedem Fall ausgefürt werden muss.
Gleichzeitig ist es ja so, dass Funktionsaufrufe auch wegoptimiert werden sollen, auch wenn sie Seiteneffekte haben, da die Seiteneffekte ja lediglich die Aufgabe haben, z.B. Caches zu befüllen, es aber um das Rückgabeergebnis geht.
Bau lieber ein großes IF draus, ist lesbarer und sicherer.
Der C++-Compiler scheint nicht beachtet zu haben, dass die Zuweisung ja trotzdem einen Seiteneffekt hat, der in jedem Fall ausgefürt werden muss.
Gleichzeitig ist es ja so, dass Funktionsaufrufe auch wegoptimiert werden sollen, auch wenn sie Seiteneffekte haben, da die Seiteneffekte ja lediglich die Aufgabe haben, z.B. Caches zu befüllen, es aber um das Rückgabeergebnis geht.
Bau lieber ein großes IF draus, ist lesbarer und sicherer.
http://fedoraproject.org/ <-- freies Betriebssystem
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
-
- Moderator
- Beiträge: 2149
- Registriert: 25.02.2009, 13:37
Re: Was erlauben C++?
Soweit ich weiß ist das undefined behaviour.
Nur die Operatoren &&, || und ?: garantieren die Reihenfolge und machen short-circuits, ansonsten sind Seiteneffekte Glückssache.
Theoretisch darf der Compiler auch jeden Funktionsaufruf anders auswerten.
Also vielleicht macht er die Zuweisung, oder auch nicht.
https://godbolt.org/z/17varqYfn
Nur die Operatoren &&, || und ?: garantieren die Reihenfolge und machen short-circuits, ansonsten sind Seiteneffekte Glückssache.
Theoretisch darf der Compiler auch jeden Funktionsaufruf anders auswerten.
Also vielleicht macht er die Zuweisung, oder auch nicht.
Clang und GCC machen da sogar auch was anderes, Clang hat ein conditional-move drin - siehe hier:Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
[Note 5: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. — end note]
https://godbolt.org/z/17varqYfn
- Krishty
- Establishment
- Beiträge: 8336
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Was erlauben C++?
Ja ist sie. Der fünfte Assembler-Befehl lautet xor edi, 1. Das ist direction = !direction.Alexander Kornrumpf hat geschrieben: ↑27.12.2024, 18:221) Ist die Zuweisung noch irgendwie im Ergebnis implizit und ich sehe sie nur nicht
x86 gibt das Ergebnis in EAX zurück, und EAX wird mit EDI verschmolzen via or eax, edi in der letzten Zeile.
Wenn du den Parameter als bool & auslegst, so dass der Compiler gezwungen ist, die Nebenwirkungen öfter zu realisieren, siehst du es etwas deutlicher.
Rest nachher
- Krishty
- Establishment
- Beiträge: 8336
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Was erlauben C++?
Einspruch!NytroX hat geschrieben: ↑27.12.2024, 20:04 Soweit ich weiß ist das undefined behaviour.
Nur die Operatoren &&, || und ?: garantieren die Reihenfolge und machen short-circuits, ansonsten sind Seiteneffekte Glückssache.
Theoretisch darf der Compiler auch jeden Funktionsaufruf anders auswerten.
Also vielleicht macht er die Zuweisung, oder auch nicht.
C++ hat das Konzept von Sequence Points. Das sind festgelegte Punkte, an denen alle Wirkungen realisiert sein müssen. Der häufigste Sequence Point ist das Semikolon.
int i = 0;
i++;
i hat nach dem ersten Semikolon den Wert 0 und nach dem zweiten den Wert 1.
Neben Semikolon sind bspw. auch if, && und Funktionsaufrufe Sequence Points. Damit sowas wie if(i-- && foo(i++)) funktioniert wie man erwartet.
Variablen dürfen IIRC innerhalb eines Sequence Points nur einmal modifiziert und ausgewertet werden*. Sonst Undefined Behavior. Bspw. int j = i++
+ ++i; – das meinst du.
Operator ? : hat AFAIK einen Sequence Point nach dem Auswerten des vorderen Ausdrucks. Das Verketten von ? : ist also legal, sogar wenn die Verkettung mehrfach die selbe Variable liest und schreibt. Jedes Erreichen eines ? muss dann alle Variablen-Werte realisiert haben. Nur innerhalb des selben ? : die Variable mehrfach modifizieren etc. wäre undefined.
- Krishty
- Establishment
- Beiträge: 8336
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Was erlauben C++?
Und jetzt ganz abschließen: Die Sequence Points bedeuten, dass der Compiler die Invertierung von direction realisiert haben muss, wenn das innerste ? erreicht wird. Unterstrichen:
int dx = (!direction && x > 20) ? -1
: (direction && x < 200) ? 1
: 0 & (direction = !direction);
Das wird nur erreicht, wenn die erste Prüfung false ergibt. Darum ist xor edi, 1 im Assembly hinter dem Sprung je .L1.
int dx = (!direction && x > 20) ? -1
: (direction && x < 200) ? 1
: 0 & (direction = !direction);
Das wird nur erreicht, wenn die erste Prüfung false ergibt. Darum ist xor edi, 1 im Assembly hinter dem Sprung je .L1.
- dot
- Establishment
- Beiträge: 1746
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: Was erlauben C++?
nicht mehr; seit C++11 basiert die Spezifikation auf sequenced before relationships ;)
aber ja [expr.cond]/1
The first expression is sequenced before the second or third expression ([intro.execution]).
-
- Moderator
- Beiträge: 2149
- Registriert: 25.02.2009, 13:37
Re: Was erlauben C++?
Danke. Verstehe ich.Krishty hat geschrieben: ↑27.12.2024, 20:41Ja ist sie. Der fünfte Assembler-Befehl lautet xor edi, 1. Das ist direction = !direction.Alexander Kornrumpf hat geschrieben: ↑27.12.2024, 18:221) Ist die Zuweisung noch irgendwie im Ergebnis implizit und ich sehe sie nur nicht
x86 gibt das Ergebnis in EAX zurück, und EAX wird mit EDI verschmolzen via or eax, edi in der letzten Zeile.
Hatte ich tatsächlich probiert, aber nicht besonders systematisch, oder so dass ich mir zugetraut hätte das Ergebnis zu interpretieren. Das Beispiel ist natürlich auch insofern künstlich als dass man ja dx nicht wegwerfen würde wenn man schon diesen Zauber macht.
Wenn du den Parameter als bool & auslegst, so dass der Compiler gezwungen ist, die Nebenwirkungen öfter zu realisieren, siehst du es etwas deutlicher.
Rest nachher