Wszystkie procesy projektowe prowadzą do kompromisów między wzajemnie niekompatybilnymi celami. Niestety, proces projektowania przeciążonego &&
operatora w C ++ dał mylący wynik końcowy: &&
pomijano tę samą funkcję, której oczekujesz - jej działanie zwarciowe.
Nie znam szczegółów, jak ten proces projektowania skończył się w tym niefortunnym miejscu. Ważne jest jednak, aby zobaczyć, jak późniejszy proces projektowania wziął pod uwagę ten nieprzyjemny wynik. W języku C # przeciążony &&
operator powoduje zwarcie. Jak projektanci C # to osiągnęli?
Jedna z pozostałych odpowiedzi sugeruje „podnoszenie lambda”. To jest:
A && B
mogłoby być postrzegane jako coś moralnego odpowiednika:
operator_&& ( A, ()=> B )
gdzie drugi argument używa pewnego mechanizmu leniwego oceniania, tak że po oszacowaniu powstają skutki uboczne i wartość wyrażenia. Implementacja przeciążonego operatora spowoduje wykonanie leniwej oceny tylko wtedy, gdy jest to konieczne.
To nie jest to, co zrobił zespół projektowy C #. (Na marginesie: chociaż podnoszenie lambda jest tym, co zrobiłem, gdy przyszedł czas na przedstawienie drzewa wyrażeń tego ??
operatora, który wymaga pewnych operacji konwersji być wykonywane leniwie Opisując to w szczegółach będzie jednak głównym dygresja wystarczy powiedzieć:.. Lambda podnoszenia działa, ale jest na tyle ciężki, że chcieliśmy tego uniknąć).
Zamiast tego, rozwiązanie C # dzieli problem na dwa oddzielne problemy:
- czy powinniśmy ocenić prawostronny operand?
- jeśli odpowiedź na powyższe pytanie brzmiała „tak”, to jak połączyć te dwa operandy?
Dlatego problem rozwiązuje się, uniemożliwiając &&
bezpośrednie przeciążanie . Zamiast tego w C # należy przeciążać dwa operatory, z których każdy odpowiada na jedno z tych dwóch pytań.
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 }
...
(Na marginesie: właściwie trzy. C # wymaga tego if operator false
podano operator, to operatortrue
należy również podać , co odpowiada na pytanie: czy to jest „prawda-prawda?”. Zwykle nie byłoby powodu, aby podawać tylko jeden taki operator, więc C # wymaga obu.)
Rozważ oświadczenie w formie:
C cresult = cleft && cright;
Kompilator generuje kod w tym celu, tak jak myślałeś, że napisałeś ten pseudo-C #:
C cresult;
C tempLeft = cleft;
cresult = C.false(tempLeft) ? tempLeft : C.&(tempLeft, cright);
Jak widać, zawsze oceniana jest lewa strona. Jeśli okaże się, że jest to „fałszywe”, to jest to wynik. W przeciwnym razie oceniana jest prawa strona i chętny operator zdefiniowany przez użytkownika&
wywoływany jest .
||
Operator jest zdefiniowana w sposób analogiczny, jak wywołanie operatora prawdziwym i chętnie |
operatora:
cresult = C.true(tempLeft) ? tempLeft : C.|(tempLeft , cright);
Poprzez zdefiniowanie wszystkich czterech operatorów - true
, false
, &
i |
- C # pozwala nie tylko mówią, cleft && cright
ale też nie zwarcie cleft & cright
, a także if (cleft) if (cright) ...
, a c ? consequence : alternative
iwhile(c)
, i tak dalej.
Teraz powiedziałem, że wszystkie procesy projektowe są wynikiem kompromisu. Tutaj projektantom języka C # udało się uzyskać zwarcie &&
i ||
poprawnie, ale zrobienie tego wymaga przeciążenia czterech operatorów zamiast dwóch , co niektórzy uważają za mylące. Operator true / false funkcja jest jedną z najmniej zrozumiałych funkcji w języku C #. Celowi posiadania rozsądnego i prostego języka, który jest znany użytkownikom C ++, sprzeciwiły się pragnienia tworzenia krótkich obwodów i chęć nie implementowania podnoszenia lambda lub innych form leniwej oceny. Myślę, że było to rozsądne stanowisko kompromisowe, ale ważne jest, aby zdać sobie sprawę, że jest to to stanowisko kompromisowe. Po prostu inny pozycja kompromisowa , na którą zdecydowali się projektanci C ++.
Jeśli temat projektowania języka dla takich operatorów Cię interesuje, rozważ przeczytanie mojej serii na temat tego, dlaczego C # nie definiuje tych operatorów na wartościach logicznych dopuszczających wartość zerową:
http://ericlippert.com/2012/03/26/null-is-not-false-part-one/
operator&&(const Foo& lhs, const Foo& rhs) : (lhs.bars == 0)