Alle Entwurfsprozesse führen zu Kompromissen zwischen miteinander inkompatiblen Zielen. Leider hat der Entwurfsprozess für den überladenen &&
Operator in C ++ zu einem verwirrenden Endergebnis geführt: Die gewünschte Funktion &&
- das Kurzschlussverhalten - entfällt.
Die Details, wie dieser Designprozess an diesem unglücklichen Ort endete, die ich nicht kenne. Es ist jedoch wichtig zu sehen, wie ein späterer Entwurfsprozess dieses unangenehme Ergebnis berücksichtigt hat. In C #, der überladenen &&
Operator ist Kurzschluss. Wie haben die Designer von C # das erreicht?
Eine der anderen Antworten schlägt "Lambda-Heben" vor. Das ist:
A && B
könnte als etwas moralisch Äquivalentes verwirklicht werden als:
operator_&& ( A, ()=> B )
wobei das zweite Argument einen Mechanismus für die verzögerte Bewertung verwendet, so dass bei der Bewertung die Nebenwirkungen und der Wert des Ausdrucks erzeugt werden. Die Implementierung des überlasteten Operators würde die verzögerte Auswertung nur bei Bedarf durchführen.
Dies hat das C # -Design-Team nicht getan. (Abgesehen davon: Lambda-Lifting ist das, was ich getan habe, als es an der Zeit war, eine Ausdrucksbaumdarstellung des ??
Operators durchzuführen, bei der bestimmte Konvertierungsvorgänge träge ausgeführt werden müssen. Eine detaillierte Beschreibung wäre jedoch ein großer Exkurs. Es genügt zu sagen: Lambda-Lifting funktioniert aber ist so schwer, dass wir es vermeiden wollten.)
Die C # -Lösung unterteilt das Problem vielmehr in zwei separate Probleme:
- sollten wir den rechten Operanden bewerten?
- Wenn die Antwort auf das oben Gesagte "Ja" war, wie kombinieren wir dann die beiden Operanden?
Daher wird das Problem dadurch gelöst, dass eine &&
direkte Überlastung illegal ist . Vielmehr müssen Sie in C # zwei Operatoren überladen , von denen jeder eine dieser beiden Fragen beantwortet.
class C
{
// Is this thing "false-ish"? If yes, we can skip computing the right
// hand size of an &&
public static bool operator false (C c) { whatever }
// If we didn't skip the RHS, how do we combine them?
public static C operator & (C left, C right) { whatever }
...
(Abgesehen davon: tatsächlich drei. C # erfordert, dass, wenn ein Operator false
bereitgestellt wird, true
auch ein Operator bereitgestellt werden muss, was die Frage beantwortet: Ist dieses Ding "wahr"? Typischerweise gibt es keinen Grund, nur einen solchen Operator bereitzustellen, also C # erfordert beides.)
Betrachten Sie eine Erklärung des Formulars:
C cresult = cleft && cright;
Der Compiler generiert Code dafür, als ob Sie dieses Pseudo-C # geschrieben hätten:
C cresult;
C tempLeft = cleft;
cresult = C.false(tempLeft) ? tempLeft : C.&(tempLeft, cright);
Wie Sie sehen können, wird die linke Seite immer ausgewertet. Wenn festgestellt wird, dass es sich um "falsch" handelt, ist dies das Ergebnis. Andernfalls wird die rechte Seite ausgewertet und der eifrige benutzerdefinierte Operator &
aufgerufen.
Der ||
Operator wird in analoger Weise als Aufruf des Operators true und des eifrigen |
Operators definiert:
cresult = C.true(tempLeft) ? tempLeft : C.|(tempLeft , cright);
Durch die Definition alle vier Operatoren - true
, false
, &
und |
- C # können Sie nicht nur sagen , cleft && cright
sondern auch nicht-Kurzschluss cleft & cright
, und auch if (cleft) if (cright) ...
, und c ? consequence : alternative
und while(c)
und so weiter.
Jetzt sagte ich, dass alle Designprozesse das Ergebnis von Kompromissen sind. Hier haben die C # -Sprachendesigner es geschafft, kurzgeschlossen &&
und ||
richtig zu werden, aber dazu müssen vier statt zwei Operatoren überlastet werden , was manche Leute verwirrend finden. Die True / False-Funktion des Operators ist eine der am wenigsten verstandenen Funktionen in C #. Dem Ziel, eine vernünftige und unkomplizierte Sprache zu haben, die C ++ - Benutzern vertraut ist, wurde der Wunsch nach Kurzschluss und der Wunsch, kein Lambda-Lifting oder andere Formen der faulen Bewertung zu implementieren, entgegengesetzt. Ich denke, das war eine vernünftige Kompromissposition, aber es ist wichtig zu erkennen, dass es sich um eine Kompromissposition handelt. Einfach anders Kompromissposition als die Designer von C ++ gelandet.
Wenn Sie das Thema Sprachdesign für solche Operatoren interessiert, lesen Sie in meiner Reihe nach, warum C # diese Operatoren nicht für nullfähige Boolesche Werte definiert:
http://ericlippert.com/2012/03/26/null-is-not-false-part-one/
operator&&(const Foo& lhs, const Foo& rhs) : (lhs.bars == 0)