Chaining Bool values give opposite result to expected

后端 未结 5 1905
忘掉有多难
忘掉有多难 2020-12-17 15:32

Unthinkingly I wrote some code to check that all the values of a struct were set to 0. To accomplish this I used:

bool IsValid() {
    return !(0 == year ==          


        
相关标签:
5条回答
  • 2020-12-17 15:39

    Your error here is writing a mathematical expression using equals signs, and unthinkingly supposing that the computer will perform the test you meant - what a human mathematician would see as the meaning of those symbols. What the computer does (as per the definition of the language) is to perform a series of discrete comparisons, each of which returns true or false - and this true or false is then used in the next comparison. You aren't comparing all of those variables to 0, you're comparing each (bar two of them) to the result of comparing another two of the said variables.

    0 讨论(0)
  • 2020-12-17 15:40

    == groups from left to right, so if all values are zero then:

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

    And so on.

    In general, x == y == z is not equivalent to x == y && x == z as you seem to expect.

    0 讨论(0)
  • 2020-12-17 15:41

    You have to consider how it's evaluated...

    a == b == c
    

    is asking if two of them are equal (a and b), then comparing that boolean result to the third value c! It is NOT comparing the first two values with the third. Anything beyond 2 arguments won't chain as you evidently expect.

    For whatever it's worth, because C++ considers non-0 values to be "true" in a boolean context, you can express what you want simply as:

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

    (note: your revised code says "month" twice and doesn't test minute).

    Back to the chained ==s: with user-defined types and operator overloading you can create a class that compares as you expect (and it can even allow things like 0 <= x < 10 to "work" in the way it's read in mathematics), but creating something special will just confuse other programmers who already know the (weird) way these things work for builtin types in C++. Worth doing as a ten/twenty minute programming exercise though if you're keen to learn C++ in depth (hint: you need the comparison operators to return a proxy object that remembers what will be the left-hand-side value for the next comparison operator).

    Finally, sometimes these "weird" boolean expressions are useful: for example, a == b == (c == d) might be phrased in English as "either (a == b) and (c == d), OR (a != b) and (c != d)", or perhaps "the equivalence of a and b is the same as the equivalence of c and d (whether true or false doesn't matter)". That might model real world situations like a double-dating scenario: if a likes/dislikes b (their date) as much as c likes/dislikes d, then they'll either hang around and have a nice time or call it quits quickly and it's painless either way... otherwise one couple will have a very tedious time of it.... Because these things can make sense, it's impossible for the compiler to know you didn't intend to create such an expression.

    0 讨论(0)
  • 2020-12-17 15:43

    The return of the == operator is 1 if the operands are equal, so regardless wether this is read from left to right or right to left, this will not do what you expect.

    so this could only work in an analogous test if you would be interested if all values are 1.

    And to have a shorter expression since you seem interested in that just do year || day || ...

    0 讨论(0)
  • 2020-12-17 15:44

    The behaviour shouldn't be seen as odd. The grammar rules for == (and most but not all binary operators) specify left to right grouping so your original expression is equivalent to:

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

    Note that when compared to an integer type a bool expression with value true will promote to 1 and with value false will promote to 0. (In C the result of the equality operator is an int in any case with a value or either 1 or 0.)

    This means that, for example, ((0 == year) == month) will be true if year is zero and month is one or if year is non-zero but month is zero and false otherwise.

    0 讨论(0)
提交回复
热议问题