Всі процеси проектування призводять до компромісів між взаємно несумісними цілями. На жаль, процес проектування перевантаженого &&
оператора в C ++ дав заплутаний кінцевий результат: те, що сама функція, від якої ви хочете, &&
- її поведінка в короткому замиканні - опущена.
Деталі того, як цей процес дизайну опинився в цьому нещасному місці, ті, кого я не знаю. Однак важливо подивитися, як пізніше процес проектування врахував цей неприємний результат. У C # перевантажений &&
оператор має коротке замикання. Як дизайнери C # домоглися цього?
Одна з інших відповідей пропонує «лямбда-ліфтинг». Це є:
A && B
можна реалізувати як щось морально рівнозначне:
operator_&& ( A, ()=> B )
де другий аргумент використовує певний механізм для ледачої оцінки, щоб при оцінці вироблялися побічні ефекти та значення виразу. Реалізація перевантаженого оператора зробить лише ледачу оцінку лише в разі необхідності.
Це не те, що зробила команда дизайнерів C #. ( В стороні: хоча лямбда - ліфтинг є те , що я зробив , коли прийшов час , щоб зробити вираз деревовидного уявлення про ??
оператора, який вимагає певних конверсійних операцій повинен виконуватися ліниво Описуючи , що в деталях б , однак, головним екскурс Досить сказати: .. Лямбда - ліфтинг працює, але є достатньо важкою, що ми хотіли цього уникнути.)
Скоріше, рішення C # розбиває проблему на дві окремі проблеми:
- чи слід оцінювати правий операнд?
- якщо відповідь на вищесказане було «так», то як ми поєднаємо два операнди?
Тому проблема вирішується шляхом незаконного перевантаження &&
безпосередньо. Скоріше, в C # ви повинні перевантажити двох операторів, кожен з яких відповідає на одне з цих двох питань.
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 }
...
(Окрім: насправді, три. C # вимагає, що якщо оператор false
надається, тоді true
також повинен бути наданий оператор , який відповідає на питання: чи ця річ є "справжньою?"? вимагає обох.)
Розглянемо виклад форми:
C cresult = cleft && cright;
Компілятор генерує код для цього, як вважав, що ви написали цей псевдо-C #:
C cresult;
C tempLeft = cleft;
cresult = C.false(tempLeft) ? tempLeft : C.&(tempLeft, cright);
Як бачите, ліва частина завжди оцінюється. Якщо він визначається як "помилковий результат", то це результат. В іншому випадку оцінюється права частина, і викликається прагнення визначеного користувачем оператора &
.
||
Оператор визначено в аналогічному образі, як виклик оператора істинний і нетерплячий |
оператор:
cresult = C.true(tempLeft) ? tempLeft : C.|(tempLeft , cright);
Визначивши всі чотири оператори - true
, false
, &
і |
- C # дозволяє не тільки сказати , cleft && cright
але і без короткого замикання cleft & cright
, а також if (cleft) if (cright) ...
, і , c ? consequence : alternative
і while(c)
, і так далі.
Тепер я сказав, що всі дизайнерські процеси є результатом компромісу. Тут дизайнерам мови C # вдалося отримати коротке замикання &&
і ||
правильно, але це вимагає перевантаження чотирьох операторів замість двох , що дехто вважає заплутаним. Функція true / false для оператора - одна з найменш добре зрозумілих особливостей у C #. Ціль володіння розумною та зрозумілою мовою, яка знайома користувачам C ++, протистояла бажанню короткого замикання та прагненню не застосовувати лямбда-ліфтинг чи інші форми ледачого оцінювання. Я думаю , що це розумний компроміс положення, але важливо розуміти , що це є компромісом положення. Просто інше компромісна позиція, ніж посадили дизайнери C ++.
Якщо тема мовного дизайну для таких операторів вас зацікавила, розгляньте, прочитавши мою серію про те, чому C # не визначає цих операторів на незмінні булеві:
http://ericlippert.com/2012/03/26/null-is-not-false-part-one/
operator&&(const Foo& lhs, const Foo& rhs) : (lhs.bars == 0)