二元布尔运算符具有结合性吗?
-
27-12-2019 - |
题
是 a && b && c
由语言定义的意思 (a && b) && c
或者 a && (b && c)
?
哇,杰瑞速度真快。为了加强这个问题:这真的重要吗?之间是否存在可观察到的差异 a && b && c
被解释为 (a && b) && c
或者 a && (b && c)
?
解决方案
§5.14/1:“&& 运算符从左到右分组。...]与&&&保证从左到右评估:如果第一个操作数为 false,则不会计算第二个操作数。”
至于何时或如何重要:我不确定它是否真的适用于内置类型。然而,有可能以某种方式使其超载,从而使其变得重要。例如:
#include <iostream>
class A;
class M {
int x;
public:
M(int x) : x(x) {}
M &operator&&(M const &r);
M &operator&&(A const &r);
friend class A;
};
class A {
int x;
public:
A(int x) : x(x) {}
A &operator&&(M const &r);
A &operator&&(A const &r);
operator int() { return x;}
friend class M;
};
M & M::operator&&(M const &r) {
x *= r.x;
return *this;
}
M & M::operator&&(A const &r) {
x *= r.x;
return *this;
}
A &A::operator&&(M const &r) {
x += r.x;
return *this;
}
A &A::operator&&(A const &r) {
x += r.x;
return *this;
}
int main() {
A a(2), b(3);
M c(4);
std::cout << ((a && b) && c) << "\n";
std::cout << (a && (b && c)) << "\n";
}
结果:
9
16
警告:这仅表明它是如何 能 变得重要。我是 不是 特别建议任何人这样做,只是表明,如果你足够渴望,你可以创造一种能够产生影响的情况。
其他提示
这 &&
和 ||
操作员短路:如果左侧的操作数决定了整个表达式的结果,则右侧的操作数甚至不会被计算。
因此,数学家会将它们描述为左结合的,例如
a && b && c
⇔ (a && b) && c
, ,因为对于数学家来说这意味着 a
和 b
被首先考虑。不过,出于教学目的,写一下可能会有用 a && (b && c)
相反,要强调的是 两者都不 b
也不 c
将被评估,如果 a
是假的。
C 中的括号仅在覆盖时更改计算顺序 优先级. 。两个都 a && (b && c)
和 (a && b) && c
将被评估为 a
首先,然后 b
, , 然后 c
. 。同样,两者的评估顺序
a + (b + c)
和 (a + b) + c
未指定。对比 a + (b * c)
相对 (a + b) * c
, ,编译器仍然可以自由地评估 a
, b
, , 和 c
顺序任意,但括号决定先进行乘法还是先加法。还要对比 FORTRAN,其中至少在某些情况下必须首先计算括号表达式。
事实上,表达式从左到右计算是非常重要的。这在某些表达式中用于短路。这是一个确实很重要的案例:
vector<vector<int> > a;
if (!a.empty() && !a[0].empty() && a[0].back() == 3)
我敢打赌你每天都会写几次类似的陈述。如果未定义关联性,您将遇到巨大的麻烦。
如果 a && b
是假的,那么 && c
部分从未经过测试。所以是的,这确实很重要(至少因为您需要从左到右对操作进行排序)。 &&
本质上是一种结合运算。
关联属性
在同一关联操作员的一排中包含两个或多个出现的表达式中,只要操作数的序列不更改,执行操作的顺序就无关紧要。也就是说,在这种表达式中重新排列括号不会改变其值。
所以如果你把它写成(从逻辑上来说)并不重要 a && (b && c)
或者 (a && b) && c
. 。两者是等价的。但你不能改变操作的顺序(例如 a && c && b
不等于 a && b && c
).