Os operadores booleanos binários têm associatividade?
-
27-12-2019 - |
Pergunta
É a && b && c
definido pela linguagem para significar (a && b) && c
ou a && (b && c)
?
Uau, Jerry foi rápido.Para reforçar a questão:isso realmente importa?Haveria uma diferença observável entre a && b && c
sendo interpretado como (a && b) && c
ou a && (b && c)
?
Solução
§5.14/1:"O operador && agrupa da esquerda para a direita....] ao contrário de & && garante avaliação da esquerda para a direita:o segundo operando não é avaliado se o primeiro operando for falso."
Quanto a quando ou como isso importa:Não tenho certeza se isso realmente acontece com os tipos integrados.É possível, no entanto, sobrecarregá-lo de uma forma que faça com que tenha importância.Por exemplo:
#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";
}
Resultado:
9
16
Embargo:isso só mostra como é pode ser feito para importar.Eu sou não recomendando particularmente que qualquer pessoa o faça, apenas mostrando que, se você quiser muito, poderá criar uma situação em que isso faça a diferença.
Outras dicas
O &&
e ||
curto-circuito dos operadores:se o operando à esquerda determinar o resultado da expressão geral, o operando à direita nem mesmo será avaliado.
Portanto, um matemático os descreveria como associativos à esquerda, por ex.
a && b && c
⇔ (a && b) && c
, porque para um matemático isso significa a
e b
são considerados em primeiro lugar.Para fins pedagógicos, porém, pode ser útil escrever a && (b && c)
em vez disso, para enfatizar que nenhum b
nem c
será avaliado se a
é falso.
Os parênteses em C só alteram a ordem de avaliação quando substituem precedência.Ambos a && (b && c)
e (a && b) && c
será avaliado como a
primeiro, então b
, então c
.Da mesma forma, a ordem de avaliação de ambos
a + (b + c)
e (a + b) + c
não é especificado.Contraste a + (b * c)
contra (a + b) * c
, onde o compilador ainda está livre para avaliar a
, b
, e c
em qualquer ordem, mas os parênteses determinam se a multiplicação ou a adição acontece primeiro.Compare também o FORTRAN, onde em pelo menos alguns casos as expressões entre parênteses devem ser avaliadas primeiro.
Na verdade é muito importante que a expressão seja calculada da esquerda para a direita.Isso é usado para causar curto-circuito em algumas expressões.Aqui está um caso em que isso importa:
vector<vector<int> > a;
if (!a.empty() && !a[0].empty() && a[0].back() == 3)
Aposto que você escreve declarações semelhantes algumas vezes por dia.E se a associatividade não fosse definida, você estaria em apuros.
Se a && b
é falso, então o && c
parte nunca é testada.Então sim, isso importa (pelo menos porque você precisa ter suas operações ordenadas da esquerda para a direita). &&
é uma operação associativa por natureza.
Propriedade associativa
Dentro de uma expressão contendo duas ou mais ocorrências em uma linha do mesmo operador associativo, a ordem em que as operações são realizadas não importa, desde que a sequência dos operandos não seja alterada.Ou seja, reorganizar os parênteses nessa expressão não alterará seu valor.
Portanto, não importa (logicamente) se você escrever como a && (b && c)
ou (a && b) && c
.Ambos são equivalentes.Mas você não pode alterar a ordem das operações (por exemplo a && c && b
não é equivalente a a && b && c
).