Значения Chain Bool дают противоположный результат ожидаемому - PullRequest
15 голосов
/ 09 мая 2011

Не задумываясь, я написал некоторый код, чтобы проверить, что все значения структуры были установлены на 0. Для этого я использовал:

bool IsValid() {
    return !(0 == year == month == day == hour == minute == second);
}

где все члены структуры имели тип unsigned short. Я использовал код как часть более крупного теста, но заметил, что он возвращает false для значений, отличающихся от нуля, и true для значений, которые были равны нулю - в противоположность тому, что я ожидал.

Я изменил код так:

bool IsValid() {
    return (0 != year) || (0 != month) || (0 != day) || (0 != hour) || (0 != minute) || (0 != second);
}

Но хотелось бы знать, что вызвало странное поведение. Это результат приоритета? Я попытался найти этот ответ в Google, но ничего не нашел, если есть какая-либо номенклатура, чтобы описать результат, который я хотел бы узнать.

Я скомпилировал код, используя VS9 и VS8.

Ответы [ 5 ]

28 голосов
/ 09 мая 2011

== групп слева направо, поэтому, если все значения равны нулю, то:

0 == year // true
(0 == year) == month // false, since month is 0 and (0 == year) converts to 1
((0 == year) == month) == day // true

и т. Д.

В общем случае x == y == z равно , а не эквивалентно x == y && x == z, как вы, вероятно, ожидаете.

15 голосов
/ 09 мая 2011

Поведение не должно быть странным.Правила грамматики для == (и большинства, но не всех бинарных операторов) задают группировку слева направо, поэтому ваше исходное выражение эквивалентно:

!((((((0 == year) == month) == day) == hour) == minute) == second)

Обратите внимание, что при сравнении с целочисленным типом a boolвыражение со значением true повысится до 1 и со значением false повысится до 0.(В C результатом оператора равенства является int в любом случае со значением или 1 или 0.)

Это означает, что, например, ((0 == year) == month) будет истиннымесли year равно нулю и month равно единице или если year не равно нулю, но month равно нулю и ложно в противном случае.

8 голосов
/ 09 мая 2011

Вы должны рассмотреть, как оно оценивается ...

a == b == c

спрашивает, равны ли два из них (a и b), затем сравнивает этот логический результат с третьим значением c!Это НЕ сравнивает первые два значения с третьим.Все, что находится за пределами двух аргументов, не будет цепляться, как вы, очевидно, ожидаете.

Сколько бы это ни стоило, поскольку C ++ считает ненулевые значения «истинными» в логическом контексте, вы можете выразить то, что хотите, просто:

return year && month && day && hour && minute && second;

(примечание: ваш пересмотренный код дважды говорит "месяц" и не проверяет минуты).

Назад к цепочке == s: с пользовательскими типами и операторомперегрузка может создать класс, который сравнивается, как вы ожидаете (и он даже может позволить вещам вроде 0 <= x < 10 «работать» так, как это читается в математике), но создание чего-то особенного просто запутает других программистовкто уже знает (странный) способ, которым эти вещи работают для встроенных типов в C ++.Тем не менее, стоит выполнить упражнение по программированию на десять / двадцать минут, если вы заинтересованы в глубоком изучении C ++ (подсказка: вам нужны операторы сравнения, чтобы вернуть прокси-объект, который запоминает значение слева для следующего сравнения).оператор).

Наконец, иногда эти «странные» логические выражения полезны: например, a == b == (c == d) может быть выражено на английском языке как «либо (a == b) и (c == d), либо ИЛИ(a! = b) и (c! = d) ", или, возможно," эквивалентность a и b такая же, как эквивалентность c и d (независимо от того, имеет ли значение true или false) ".Это может моделировать ситуации реального мира, такие как сценарий двойного знакомства: если a любит / не любит b (их дата) столько же, сколько c любит / не любит d, то они либо будут торчать и хорошо проводить время, либо назовут это быстро ив любом случае это безболезненно ... иначе у одной пары будет очень утомительное время .... Поскольку эти вещи могут иметь смысл, компилятор не может знать, что вы не собирались создавать такое выражение.

1 голос
/ 09 мая 2011

Ваша ошибка здесь заключается в написании математического выражения с использованием знаков равенства и бездумного предположения, что компьютер выполнит тест, который вы имели в виду - что математик-человек увидит в значении этих символов. Компьютер выполняет (в соответствии с определением языка) выполнение серии дискретных сравнений, каждое из которых возвращает true или false - и это true или false затем используется в следующее сравнение . Вы не сравниваете все эти переменные с 0, вы сравниваете каждую (кроме двух) с результатом сравнения двух других из указанных переменных.

1 голос
/ 09 мая 2011

Возвращение оператора == равно 1, если операнды равны, поэтому независимо от того, читается ли это слева направо или справа налево, это не будет делать то, что вы ожидаете.

так что это может работать только в аналогичном тесте, если вам будет интересно, если все значения равны 1.

И чтобы иметь более короткое выражение, поскольку вы, кажется, заинтересованы в этом, просто сделайте year || day || ...

...