Все процессы проектирования приводят к компромиссу между несовместимыми целями. К сожалению, процесс разработки перегруженного &&
оператора в C ++ дал запутанный конечный результат: то, что вам нужно, &&
- его поведение при коротком замыкании - пропущено.
Подробностей того, как этот процесс проектирования оказался в этом неудачном месте, я не знаю. Однако уместно посмотреть, как более поздний процесс проектирования учел этот неприятный результат. В C #, перегруженный &&
оператор является коротким замыканием. Как разработчики C # достигли этого?
Один из других ответов предполагает «лямбда-лифтинг». То есть:
A && B
может быть реализовано как нечто морально эквивалентное:
operator_&& ( A, ()=> B )
где второй аргумент использует некоторый механизм для ленивой оценки, так что при оценке производятся побочные эффекты и значение выражения. Реализация перегруженного оператора будет выполнять ленивую оценку только при необходимости.
Это не то, что сделала группа разработчиков C #. ( В стороне: хотя лямбда - лифтинг является то , что я сделал , когда пришло время , чтобы сделать выражение древовидного представления о ??
операторе, который требует определенных конверсионных операций должен выполняться лениво Описывая , что в деталях бы , однако, главным экскурс Достаточно сказать:.. Лямбда - лифтинг работает, но достаточно тяжелый, поэтому мы хотели этого избежать.)
Скорее решение C # разбивает проблему на две отдельные проблемы:
- мы должны оценить правый операнд?
- если ответ на предыдущий был «да», то как нам объединить два операнда?
Поэтому проблема решается путем запрета &&
прямой перегрузки . Вместо этого в C # вы должны перегрузить два оператора, каждый из которых отвечает на один из этих двух вопросов.
class C
{
public static bool operator false (C c) { whatever }
public static C operator & (C left, C right) { whatever }
...
(Кроме того: на самом деле их три. C # требует, чтобы если оператор false
был предоставлен, то оператор true
также должен был быть предоставлен, что отвечает на вопрос: «Верно ли это?». Как правило, нет причин предоставлять только один такой оператор, поэтому C # требует обоих.)
Рассмотрим выписку вида:
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 # удалось добиться правильного &&
и короткого замыкания ||
, но для этого требуется перегрузка четырех операторов вместо двух , что некоторых людей сбивает с толку. Функция оператора "истина / ложь" - одна из наименее понятных функций C #. Целью создания разумного и простого языка, знакомого пользователям C ++, противостояли стремление к короткому замыканию и желание не реализовывать лямбда-лифтинг или другие формы ленивого вычисления. Я думаю , что это разумный компромисс положение, но важно понимать , что это является компромиссом положение. Просто другой компромиссная позиция, на которую остановились разработчики C ++.
Если вас интересует тема проектирования языков для таких операторов, подумайте о прочтении моей серии статей о том, почему C # не определяет эти операторы в логических значениях, допускающих значение NULL:
http://ericlippert.com/2012/03/26/null-is-not-false-part-one/
operator&&(const Foo& lhs, const Foo& rhs) : (lhs.bars == 0)