二元布尔运算符是否具有关联性?

时间:2013-11-18 16:41:47

标签: c++ boolean operators associativity

该语言定义的a && b && c(a && b) && c还是a && (b && c)

哇,杰瑞很快。加强这个问题:它真的重要吗? a && b && c被解释为(a && b) && ca && (b && c)之间是否存在可观察到的差异?

4 个答案:

答案 0 :(得分:10)

§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

警告:这只显示如何成为重要事项。我特别推荐任何人这样做,只表明如果你想要的话,你可以创造一个有所作为的情况。

答案 1 :(得分:5)

实际上,从左到右计算表达式非常重要。这用于某些表达式中的短路。这是一个重要的案例:

vector<vector<int> > a;
if (!a.empty()  && !a[0].empty() && a[0].back() == 3) 

我打赌你每天写几次类似的陈述。如果没有定义关联性,那将会遇到很大麻烦。

答案 2 :(得分:5)

&&||运算符短路:如果左侧的操作数确定整个表达式的结果,则右侧的操作数甚至不会被评估。

因此,数学家会将它们描述为左联想,例如
a && b && c(a && b) && c,因为数学家认为ab是第一位的。但是,出于教学目的,编写a && (b && c)可能会有用,强调如果b ca都不会被评估是假的。

C中的括号仅在覆盖优先级时更改评估顺序。两个a && (b && c)
并且(a && b) && c将首先评估为a,然后b,然后c。同样,两者的评价顺序 a + (b + c)(a + b) + c未指定。对比a + (b * c)(a + b) * c,编译器仍然可以按任意顺序自由评估abc,但括号确定是否为乘法或者首先添加。还要对比FORTRAN,其中至少在某些情况下必须首先评估括号表达式。

答案 3 :(得分:2)

如果a && b为false,则永远不会测试&& c部分。所以是的,它确实很重要(至少在你需要从左到右排序你的操作)。 &&本质上是一种关联操作。

关联属性

  

在一行中包含两次或多次出现的表达式中   相同的关联运算符,即运算的顺序   只要操作数的顺序是,执行无关紧要   没有改变。也就是说,在这样的情况下重新排列括号   表达式不会改变它的价值。

因此,如果您将其写为a && (b && c)(a && b) && c,则无论如何(逻辑上)都无关紧要。两者都是等价的。但是您无法更改操作的顺序(例如a && c && b不等同于a && b && c)。