是a && b && 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)
?
哇,杰瑞很快。加强这个问题:这真的重要吗?a && b && c
被解释为(a && b) && c
或之间会有明显的区别a && (b && c)
吗?
§5.14/1:“&& 运算符从左到右分组。[...] 与 & 不同,&& 保证从左到右的评估:如果第一个操作数为假,则不评估第二个操作数。”
至于何时或如何重要:我不确定它是否真的适用于内置类型。但是,有可能以一种重要的方式对其进行超载。例如:
#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
警告:这只显示了它是如何变得重要的。我并不是特别推荐任何人这样做,只是表明如果你想做得足够好,你可以创造一种情况,它会有所作为。
事实上,从左到右计算表达式非常重要。这在某些表达式中用于短路。这是一个重要的案例:
vector<vector<int> > a;
if (!a.empty() && !a[0].empty() && a[0].back() == 3)
我敢打赌,你每天写几次类似的陈述。如果没有定义关联性,您将遇到巨大的麻烦。
和运算&&
符||
短路:如果左边的操作数决定了整个表达式的结果,右边的操作数甚至不会被计算。
因此,数学家会将它们描述为左结合,例如
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,至少在某些情况下,必须首先计算带括号的表达式。
如果a && b
为假,则&& c
永远不会测试该部分。所以是的,它确实很重要(至少你需要从左到右排序你的操作)。 &&
本质上是一种关联操作。
关联属性
在同一关联运算符的一行中包含两次或多次出现的表达式中,只要操作数的顺序不变,执行操作的顺序无关紧要。也就是说,在这样的表达式中重新排列括号不会改变它的值。
a && (b && c)
因此,如果您将其写为or并不重要(逻辑上)(a && b) && c
。两者是等价的。但是您不能更改操作的顺序(例如a && c && b
不等同于a && b && c
)。