Forum: Compiler & IDEs C++ andere Bedeutung der Mehrfachzuweisung


von Peter D. (peda)


Lesenswert?

In C++ sieht man ja oft solche Mehrfachzuweisungen:
1
counter = flankenerkennung = entprellen = taster;
Unter C ist die Bedeutung eindeutig, alle Variablen werden auf den 
gleichen Wert gesetzt.
Unter C++ ist die Bedeutung aber eine völlig andere. Wo kann man 
nachlesen, was das bedeutet?

von DPA (Gast)


Lesenswert?

Peter D. schrieb:
> In C++ sieht man ja oft solche Mehrfachzuweisungen:counter =
> flankenerkennung = entprellen = taster;
> Unter C ist die Bedeutung eindeutig, alle Variablen werden auf den
> gleichen Wert gesetzt.

Nein, dass ist nicht der fall.
1
flankenerkennung = entprellen = taster;
entspricht:
1
entprellen = taster;
2
flankenerkennung = entprellen;

Insbesondere wenn entprellen volatile ist kann sich das zwischendurch 
durchaus ändern.

von Georg B. (diereinegier)


Lesenswert?

Das wird von rechts nach links ausgeführt und wenn die beteiligten Typen 
Klassen sind, dann kann man das in der Dokumentation oder im Quelltext 
der Klasse nachlesen, die den Zuweisungsoperator und evtl. noch 
Type-Cast-Operatoren implementiert.

von Oliver S. (oliverso)


Lesenswert?

Und all das bräuchte nicht weiter diskutiert werden, wenn man solche 
Konstrukte einfach nicht nutzt.

Oliver
P.S:
Häufig sieht man das auch in C++ eigentlich überhaupt nicht. Da gibt es 
eigentlich hier nur den einen, der das Beispiel von oben auch verbrochen 
hat.

Das Beispiel zeigt genau den Grund, warum man das nicht machen soll. Das 
sieht der Verursacher zwar anders (der findet seine Idee toll), aber 
egal. Murks bleibt Murks.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?


von Olaf B. (Firma: OBUP) (obrecht)


Lesenswert?

Oliver S. schrieb:
> sieht man ... in C++ eigentlich überhaupt nicht
wird bei Ruby öfters verwandt. In C++ nicht.

von Oliver S. (oliverso)


Lesenswert?

Das o.a. Beispiel stammt von hier:

Beitrag "Re: C/C++ Objektmethoden"

und basiert auf unerwartetem Operator-Overloading. Die Grundregel des 
"principle of least surprise", die das verletzt, gilt genauso für Rust 
und andere Programmiersprachen.

Oliver

von Olaf B. (Firma: OBUP) (obrecht)


Lesenswert?

bei C++ FACK

bei Ruby meckert nicht mal der Linter, der dort den Code checkt. :-)

von Peter D. (peda)


Lesenswert?

Oliver S. schrieb:
> Das o.a. Beispiel stammt von hier:
>
> Beitrag "Re: C/C++ Objektmethoden"
>
> und basiert auf unerwartetem Operator-Overloading. Die Grundregel des
> "principle of least surprise", die das verletzt, gilt genauso für Rust
> und andere Programmiersprachen.

Danke für den Link, das erklärt vieles.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> In C++ sieht man ja oft solche Mehrfachzuweisungen:
>
1
> counter = flankenerkennung = entprellen = taster;
2
>
> Unter C ist die Bedeutung eindeutig, alle Variablen werden auf den
> gleichen Wert gesetzt.
> Unter C++ ist die Bedeutung aber eine völlig andere. Wo kann man
> nachlesen, was das bedeutet?

Der Kopierzuweisungsoperator ist ein C++ ein Operator, den man selbst 
definieren und überladen kann. Was der Operator dann tut, ist der 
Sprache egal. Allerdings (wie oben schon geschrieben), sollte man dabei 
die übliche Erwartungshaltung erfüllen, und zwar der Operator (etwa bei: 
lhs = rhs) hat als Resultat das lhs-Objekt und übt einen bestimmten 
Seiteneffekt auf lhs aus, nämlich kopiert die non-const Attribute.

von Yalu X. (yalu) (Moderator)


Lesenswert?

DPA schrieb:
> Peter D. schrieb:
>> In C++ sieht man ja oft solche Mehrfachzuweisungen:counter =
>> flankenerkennung = entprellen = taster;
>> Unter C ist die Bedeutung eindeutig, alle Variablen werden auf den
>> gleichen Wert gesetzt.
>
> Nein, dass ist nicht der fall.
>
>  flankenerkennung = entprellen = taster;
>
> entspricht:
>
>  entprellen = taster;
>  flankenerkennung = entprellen;
>
> Insbesondere wenn entprellen volatile ist kann sich das zwischendurch
> durchaus ändern.

Auch implizite Typkonvertierungen können zu Unterschieden führen:

1
#include <stdio.h>
2
#include <stdint.h>
3
4
int main(void) {
5
  double d;
6
  int16_t i16;
7
  uint8_t ui8;
8
9
  ui8 = i16 = d = -1234.5;
10
11
  printf("d=%f, i16=%d, u8=%u\n", d, i16, ui8);
12
}

ergibt

1
d=-1234.500000, i16=-1234, u8=46


Olaf B. schrieb:
> wird bei Ruby öfters verwandt. In C++ nicht.

In Ruby und Python ist die Zuweisung an gewöhnliche Variablen (also
keine Membervariablen) nicht überladbar, und es gibt keine impliziten
Typkonvertierungen, weswegen es dort diesbezüglich keine Überraschungen
gibt.

Ich verwende die Mehrfachzuweisung auch in C und C++, aber nur für
primitive Datentypen (int, double usw.) und nur für Variablen gleichen
Typs. Damit sind Überraschungen ebenfalls ausgeschlossen.


Oliver S. schrieb:
> Das o.a. Beispiel stammt von hier:
>
> Beitrag "Re: C/C++ Objektmethoden"

Das ist wirklich gruselig, noch gruseliger als die Vergewaltigung der
Bitshift- als I/O-Operatoren in iostream. Hätte der Fanboy statt des =
wenigstens das ohnehin schon versaute << genommen, wäre die Überraschung
nicht ganz so heftig.

: Bearbeitet durch Moderator
von Heiko L. (zer0)


Lesenswert?

Yalu X. schrieb:
> Das ist wirklich gruselig, noch gruseliger als die Vergewaltigung der
> Bitshift- als I/O-Operatoren in iostream. Hätte der Fanboy statt des =
> wenigstens das ohnehin schon versaute << genommen, wäre die Überraschung
> nicht ganz so heftig.

Nur, dass << von links nach rechts ausgewertet wird.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Heiko L. schrieb:
> Nur, dass << von links nach rechts ausgewertet wird.

Stimmt. Dann also andersherum:

1
  taster >> entprellen >> flankenerkennung >> counter;

Das passt ohnehin besser zu der hierzulande üblichen Leserichtung.

Oder auch

1
  taster | entprellen | flankenerkennung | counter;

Das erinnert an eine Shell-Pipline, die eine ähnliche Funktion hat.

Ein Rest des bösen Überraschungseffekts bleibt aber dennoch.

Warum müssen dafür unbedingt die Operatoren missbraucht werden, wenn es
auch mit benamsten Memberfunktionen geht?

Also so:

1
taster.pipe(entprellen).pipe(flankenerkennung).pipe(counter);

Oder, wenn die Memberfunktionsnamen eher wie Infixoperatoren aussehen
sollen, so:

1
taster .pipe (entprellen) .pipe (flankenerkennung) .pipe (counter);

von Heiko L. (zer0)


Lesenswert?

Ja, richtig. Bei dem Beispiel mit den Zuweisungen würde ich davon 
ausgehen, dass das Objekt ganz Rechts, also "taster", const sein könnte. 
Beim >> würde ich darauf nicht wetten.
Insgesamt wäre ich bei Operatoren vorsichtig, ihnen eine Bedeutung zu 
geben, die nicht "vom Standard diktiert" wurde. Wenn die Bedeutung kein 
common-sense ist, führt der Verzicht auf die Benamung schlicht dazu, 
dass man nur noch mit irgendeinem wildcard-symbol konfrontiert ist. 
Schlimmer noch, als das bei benannten Funktionen z.T. schon der Fall 
ist.
Die >>-Kette gibt z.B. klassischer Weise das Objekt links zurück, nicht 
das rechte.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Heiko L. schrieb:
> Insgesamt wäre ich bei Operatoren vorsichtig, ihnen eine Bedeutung zu
> geben, die nicht "vom Standard diktiert" wurde.

Die C++ Core Guidelines haben wohl nicht ohne Grund eine eigene Regel 
dafür kreiert, und bringen das auf den Punkt:

C.61: A copy operation should copy

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Heiko L. schrieb:
> Insgesamt wäre ich bei Operatoren vorsichtig, ihnen eine Bedeutung zu
> geben, die nicht "vom Standard diktiert" wurde. Wenn die Bedeutung kein
> common-sense ist, führt der Verzicht auf die Benamung schlicht dazu,
> dass man nur noch mit irgendeinem wildcard-symbol konfrontiert ist.

Dann schau dir mal boost.spirit an. Da treiben sie die Operator Abuse 
auf die Spitze. Siehe https://de.wikipedia.org/wiki/Spirit_(Parser)

von Heiko L. (zer0)


Lesenswert?

Rolf M. schrieb:
> Heiko L. schrieb:
>> Insgesamt wäre ich bei Operatoren vorsichtig, ihnen eine Bedeutung zu
>> geben, die nicht "vom Standard diktiert" wurde. Wenn die Bedeutung kein
>> common-sense ist, führt der Verzicht auf die Benamung schlicht dazu,
>> dass man nur noch mit irgendeinem wildcard-symbol konfrontiert ist.
>
> Dann schau dir mal boost.spirit an. Da treiben sie die Operator Abuse
> auf die Spitze. Siehe https://de.wikipedia.org/wiki/Spirit_(Parser)

Wobei das ein in sich abgeschlossenes Modul ist und die Operatoren 
keinerlei Verwendung außerhalb der Definition der Grammatik anstreben.

von Wilhelm M. (wimalopaan)


Lesenswert?

Heiko L. schrieb:
> Rolf M. schrieb:
>> Heiko L. schrieb:
>>> Insgesamt wäre ich bei Operatoren vorsichtig, ihnen eine Bedeutung zu
>>> geben, die nicht "vom Standard diktiert" wurde. Wenn die Bedeutung kein
>>> common-sense ist, führt der Verzicht auf die Benamung schlicht dazu,
>>> dass man nur noch mit irgendeinem wildcard-symbol konfrontiert ist.
>>
>> Dann schau dir mal boost.spirit an. Da treiben sie die Operator Abuse
>> auf die Spitze. Siehe https://de.wikipedia.org/wiki/Spirit_(Parser)
>
> Wobei das ein in sich abgeschlossenes Modul ist und die Operatoren
> keinerlei Verwendung außerhalb der Definition der Grammatik anstreben.

Die Grammatik hat aber nichts mit der Semantik zu tun!
Da verwechselt Du was. Es gibt nichts außerhalb der Grammatik bei einer 
formalen Sprache.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.