我不假思索地编写了一些代码来检查结构体的所有值是否都设置为 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);
}
但想知道是什么导致了奇怪的行为。是优先权的结果吗?我试图用谷歌搜索这个答案,但一无所获,如果有任何术语来描述结果,我很想知道。
我使用VS9和VS8编译代码。
==
从左到右分组,因此如果所有值均为零,则:
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
,正如您所期望的那样。
这种行为不应被视为奇怪。
==
(以及大多数但不是全部二元运算符)的语法规则指定从左到右分组,因此您的原始表达式相当于:
!((((((0 == year) == month) == day) == hour) == minute) == second)
请注意,与整数类型相比,具有值
bool
的 true
表达式将提升为 1
,而具有值 false
将提升为 0
。 (在 C 语言中,相等运算符的结果在任何情况下都是带有值的 int
或 1
或 0
。)
这意味着,例如,如果
((0 == year) == month)
为零且 year
为 1,或者如果 month
非零但 year
为零,则 month
将为 true,否则为 false。
你必须考虑如何评估它......
a == b == c
询问其中两个是否相等(
a
和b
),然后将该布尔结果与第三个值c
进行比较! 它不是将前两个值与第三个值进行比较。 超过 2 个参数的任何内容都不会像您显然期望的那样链接。
无论它的价值如何,因为 C++ 在布尔上下文中认为非 0 值是“true”,所以您可以简单地表达您想要的内容:
return year && month && day && hour && minute && second;
(注意:您修改后的代码说“月”两次,并且不测试分钟)。
回到链式
==
:使用用户定义的类型和运算符重载,您可以创建一个按照您的预期进行比较的类(它甚至可以允许像0 <= x < 10
这样的东西按照其读取的方式“工作”数学),但是创建一些特殊的东西只会让其他已经知道这些东西在 C++ 中的内置类型上工作的(奇怪的)方式的程序员感到困惑。 如果您热衷于深入学习 C++,则作为十/二十分钟的编程练习值得做(提示:您需要比较运算符返回一个代理对象,该对象记住下一次比较的左侧值是什么操作员)。
最后,有时这些“奇怪”的布尔表达式很有用:例如,
a == b == (c == d)
在英语中可能会表达为“要么(a == b)和(c == d),OR(a!= b)和( c != d)”,或者“a 和 b 的等价性与 c 和 d 的等价性相同(真假无关紧要)”。 这可能会模拟现实世界的情况,例如双重约会场景:如果a喜欢/不喜欢b(他们的约会对象)与c喜欢/不喜欢d一样多,那么他们要么会闲逛并度过愉快的时光,要么很快就退出并不管怎样,这都是无痛的...否则一对夫妇将会度过一段非常乏味的时光...因为这些事情是有意义的,所以编译器不可能知道你不打算创建这样的表达式。
您在这里的错误是使用等号编写数学表达式,并且不假思索地假设计算机将执行您想要的测试 - 人类数学家会看到这些符号的含义。计算机所做的(根据语言的定义)是执行一系列离散比较,每个比较都返回
true
或 false
- 然后这个 true
或 false
然后在下一个中使用比较。您并不是将所有这些变量与 0 进行比较,而是将每个变量(除了其中两个)与比较另外两个所述变量的结果进行比较。
如果操作数相等,
==
运算符的返回值为1
,因此无论从左到右还是从右到左读取,都不会达到您的预期。
因此,如果您对所有值都感兴趣,则这只能在类似的测试中起作用。
并且要表达更简短的内容,因为您似乎对此感兴趣,只需这样做
1