【问题标题】:Chaining Bool values give opposite result to expected链接布尔值给出与预期相反的结果
【发布时间】:2011-08-21 19:08:21
【问题描述】:

我不假思索地写了一些代码来检查结构的所有值是否都设置为 0。为此我使用了:

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

所有结构成员都是无符号短类型。我将代码用作更大测试的一部分,但注意到它对于不为零的值返回 false,对于所有等于零的值返回 true - 与我的预期相反。

我把代码改成了:

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

但想知道是什么导致了奇怪的行为。是优先的结果吗?我试过用谷歌搜索这个答案,但什么也没找到,如果有任何命名法来描述我很想知道的结果。

我使用 VS9 和 VS8 编译了代码。

【问题讨论】:

  • 您无意中发现了积分促销规则。布尔值可以提升为整数,因此测试bool == int 是有效的......即使非常违反直觉。啊!
  • 请注意,'a == b == c == d == 0' 适用于 Python。

标签: c++ c compiler-construction boolean operator-precedence


【解决方案1】:

== 从左到右分组,所以如果所有值都为零,则:

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。

【讨论】:

  • 关于简单性,这是我最喜欢的答案。查尔斯·贝利确实首先准确地介绍了它,但您的回答带来了令人赞赏的清晰性。奇怪的是我认为编译器会知道我的意思。
【解决方案2】:

这种行为不应被视为奇怪。 ==(以及大多数但不是所有二元运算符)的语法规则指定从左到右分组,因此您的原始表达式相当于:

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

请注意,与整数类型相比,值为true 的bool 表达式将提升为1,而值为false 的表达式将提升为0。 (在 C 中,相等运算符的结果是 int 在任何情况下都有一个值或 1 或 0。)

这意味着,例如,如果year 为零且month 为一,或者year 非零但month 为零,则((0 == year) == month) 为真,否则为假。

【讨论】:

    【解决方案3】:

    您必须考虑如何评估它...

    a == b == c
    

    询问其中两个是否相等(a 和 b),然后将该布尔结果与第三个值 c 进行比较!它不是将前两个值与第三个值进行比较。超过 2 个参数的任何内容都不会像您预期的那样链接。

    不管它值多少钱,因为 C++ 在布尔上下文中认为非 0 值是“真”,所以你可以简单地表达你想要的:

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

    (注意:您修改后的代码说“月”两次,而不是测试分钟)。

    回到链接的==s:通过用户定义的类型和运算符重载,您可以创建一个可以按预期进行比较的类(它甚至可以允许像 0 <= x < 10 这样的东西“工作”以它在数学中的阅读方式),但是创建一些特殊的东西只会让其他已经知道这些东西在 C++ 中的内置类型工作的(奇怪的)方式的程序员感到困惑。值得做一个十/二十分钟的编程练习,但如果您热衷于深入学习 C++(提示:您需要比较运算符返回一个代理对象,该对象记住下一次比较的左侧值运算符)。

    最后,有时这些“奇怪”的布尔表达式很有用:例如,a == b == (c == d) 在英语中可能被表述为“要么 (a == b) 和 (c == d),要么 (a != b) and (c != d)”,或者“a 和 b 的等价性与 c 和 d 的等价性相同(真假无关紧要)”。这可能会模拟现实世界的情况,例如双重约会场景:如果 a 喜欢/不喜欢 b(他们的约会)和 c 喜欢/不喜欢 d 一样多,那么他们要么会闲逛并度过愉快的时光,要么会很快退出并无论哪种方式都无痛...否则一对夫妇将度过一段非常乏味的时光....因为这些事情是有道理的,编译器不可能知道您不打算创建这样的表达式。

    【讨论】:

    • 很好发现 - 我的 m 失败。我编写 C++ 已经有一段时间了,我和一名高级程序员都被这个难住了——尽管当它像你和其他人所做的那样进行推理时,它变得很明显。我们将 lint 扔到我们的代码中,但很惊讶它没有捡起它,而且编译器不知道我的意思——尽管最终是我的失败,我很高兴从中学习。谢谢。
    • @17Twenty:起初每个人都感到惊讶——看起来像数学,但表现完全不同。我想知道哪些语言做的不同(也许米兰达 - 虽然我使用它已经有 20 年了)。我在最后一段中添加了一条注释,说明为什么 lint/编译器等无法在不冒误报风险的情况下诊断这一点。干杯。
    • 可能是误报的有趣示例,但在 C++ 中,您的示例比较了两个bool,而提问者的代码将bool 与short 进行了比较。因此,C++ 编译器可以警告其中一个而不是另一个,尽管很可能还有其他示例会导致它出错。在 C 中,两者都在比较“正确的”整数类型。
    【解决方案4】:

    您的错误是使用等号编写数学表达式,并且不假思索地假设计算机将执行您想要的测试 - 人类数学家会看到这些符号的含义。计算机所做的(根据语言的定义)是执行一系列离散比较,每个比较返回 true 或 false - 然后此 true 或 false 用于下一个比较。您不是将所有这些变量都与 0 进行比较,而是将每个变量(其中两个)与比较另外两个所述变量的结果进行比较。

    【讨论】:

      【解决方案5】:

      如果操作数相等,== 运算符的返回值为 1,因此无论是从左到右还是从右到左读取,这都不会达到您的预期。

      因此,如果您对所有值都是 1 感兴趣,这只能在类似的测试中使用。

      因为您似乎对此感兴趣,所以要使用更短的表达方式,只需 year || day || ...

      【讨论】:

        猜你喜欢
        • 2017-06-15
        • 2013-07-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多