Tutti i processi di progettazione comportano compromessi tra obiettivi reciprocamente incompatibili. Sfortunatamente, il processo di progettazione per l' &&
operatore sovraccarico in C ++ ha prodotto un risultato finale confuso: che la stessa caratteristica che si desidera &&
- il suo comportamento di corto circuito - è omessa.
I dettagli di come è finito quel processo di progettazione in questo luogo sfortunato, quelli che non conosco. È tuttavia rilevante vedere come un successivo processo di progettazione abbia tenuto conto di questo risultato spiacevole. In C #, l' &&
operatore sovraccarico è in corto circuito. Come sono riusciti i progettisti di C #?
Una delle altre risposte suggerisce "sollevamento lambda". Questo è:
A && B
potrebbe essere realizzato come qualcosa moralmente equivalente a:
operator_&& ( A, ()=> B )
dove il secondo argomento utilizza alcuni meccanismi per la valutazione pigra in modo che, quando valutati, vengano prodotti gli effetti collaterali e il valore dell'espressione. L'implementazione dell'operatore sovraccarico farebbe la valutazione pigra solo quando necessario.
Questo non è ciò che ha fatto il team di progettazione di C #. (A parte: se sollevamento lambda è quello che ho fatto quando è arrivato il momento di fare rappresentazione albero espressione del ??
operatore, che richiede alcune operazioni di conversione essere effettuato pigramente Descrivendo che in dettaglio sarebbe tuttavia un importante digressione Basti dire:.. Sollevamento lambda funziona ma è sufficientemente pesante da volerlo evitare.)
Piuttosto, la soluzione C # suddivide il problema in due problemi separati:
- dovremmo valutare l'operando di destra?
- se la risposta a quanto sopra è stata "sì", come combiniamo i due operandi?
Pertanto, il problema viene risolto rendendo illegale il sovraccarico &&
diretto. Piuttosto, in C # devi sovraccaricare due operatori, ognuno dei quali risponde a una di quelle due domande.
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 }
...
(A parte: in realtà, tre. C # richiede che se false
viene fornito l'operatore, true
anche l' operatore deve essere fornito, il che risponde alla domanda: questa cosa è "vero-vero?". In genere non ci sarebbe motivo di fornire solo uno di questi operatori, quindi C # richiede entrambi.)
Considera una dichiarazione del modulo:
C cresult = cleft && cright;
Il compilatore genera codice per questo come si pensava che tu avessi scritto questo pseudo-C #:
C cresult;
C tempLeft = cleft;
cresult = C.false(tempLeft) ? tempLeft : C.&(tempLeft, cright);
Come puoi vedere, il lato sinistro viene sempre valutato. Se si determina che è "falso-ish", allora è il risultato. Altrimenti, viene valutato il lato destro e viene richiamato l' operatore desideroso definito dall'utente &
.
L' ||
operatore è definito in modo analogo, come un'invocazione dell'operatore vero e |
dell'operatore desideroso :
cresult = C.true(tempLeft) ? tempLeft : C.|(tempLeft , cright);
Con la definizione di tutti e quattro gli operatori - true
, false
, &
e |
- C # consente non solo di dire cleft && cright
, ma anche non-corto circuito cleft & cright
, e anche if (cleft) if (cright) ...
, e c ? consequence : alternative
e while(c)
, e così via.
Ora, ho detto che tutti i processi di progettazione sono il risultato di un compromesso. Qui i progettisti del linguaggio C # sono riusciti a ottenere cortocircuiti &&
e ||
giusti, ma per farlo è necessario sovraccaricare quattro operatori anziché due , cosa che alcune persone trovano confusa. La funzione true / false dell'operatore è una delle funzionalità meno conosciute in C #. L'obiettivo di avere un linguaggio sensibile e diretto, familiare agli utenti di C ++, era contrastato dai desideri di corto circuito e dal desiderio di non implementare il lambda lifting o altre forme di valutazione pigra. Penso che fosse una posizione di compromesso ragionevole, ma è importante rendersi conto che si tratta di una posizione di compromesso. Solo un diverso posizione di compromesso rispetto a quella dei progettisti di C ++.
Se l'argomento del design del linguaggio per tali operatori ti interessa, considera di leggere le mie serie sul perché C # non definisce questi operatori su valori booleani nulla:
http://ericlippert.com/2012/03/26/null-is-not-false-part-one/
operator&&(const Foo& lhs, const Foo& rhs) : (lhs.bars == 0)