Pergunta

Tem alguma "coisa muito ruim" que pode acontecer &&= e ||= foram usados ​​como açúcar sintático para bool foo = foo && bar e bool foo = foo || bar?

Foi útil?

Solução

A bool só pode ser true ou false em C++.Como tal, usando &= e |= é relativamente seguro (embora eu não goste particularmente da notação).É verdade que eles executarão operações de bits em vez de operações lógicas (e, portanto, não causarão curto-circuito), mas essas operações de bits seguem um mapeamento bem definido, que é efetivamente equivalente às operações lógicas, desde que ambos os operandos são do tipo bool.1

Ao contrário do que outras pessoas disseram aqui, um bool em C++ nunca deve ter um valor diferente, como 2.Ao atribuir esse valor a um bool, ele será convertido para true conforme o padrão.

A única maneira de colocar um valor inválido em um bool é usando reinterpret_cast em ponteiros:

int i = 2;
bool b = *reinterpret_cast<bool*>(&i);
b |= true; // MAY yield 3 (but doesn’t on my PC!)

Mas como esse código resulta em comportamento indefinido de qualquer maneira, podemos ignorar com segurança esse problema potencial na conformidade do código C++.


1 É certo que esta é uma advertência bastante grande, como ilustra o comentário de Angew:

bool b = true;
b &= 2; // yields `false`.

A razão é que b & 2 realiza promoção inteira de modo que a expressão seja então equivalente a static_cast<int>(b) & 2, o que resulta em 0, que é então convertido novamente em um bool.Então é verdade que a existência de um operator &&= melhoraria a segurança do tipo.

Outras dicas

&& e & têm semântica diferente: && não avaliará o segundo operando se o primeiro operando for false.ou sejaalgo como

flag = (ptr != NULL) && (ptr->member > 3);

é seguro, mas

flag = (ptr != NULL) & (ptr->member > 3);

não é, embora ambos os operandos sejam do tipo bool.

O mesmo é verdade para &= e |=:

flag = CheckFileExists();
flag = flag && CheckFileReadable();
flag = flag && CheckFileContents();

se comportará de maneira diferente de:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();

Resposta curta

Todos os operadores +=, -=, *=, /=, &=, |=...são aritméticos e fornecem a mesma expectativa:

x &= foo()  // We expect foo() be called whatever the value of x

No entanto, os operadores &&= e ||= seria lógico, e esses operadores podem estar sujeitos a erros porque muitos desenvolvedores esperariam foo() ser sempre chamado x &&= foo().

bool x;
// ...
x &&= foo();           // Many developers might be confused
x = x && foo();        // Still confusing but correct
x = x ? foo() : x;     // Understandable
x = x ? foo() : false; // Understandable
if (x) x = foo();      // Obvious
  • Será que realmente precisamos tornar o C/C++ ainda mais complexo para obter um atalho para x = x && foo()?

  • Queremos realmente ofuscar ainda mais a declaração enigmática x = x && foo()?
    Ou queremos escrever um código significativo como if (x) x = foo();?


Resposta longa

Exemplo para &&=

Se &&= operador estava disponível, então este código:

bool ok = true; //becomes false when at least a function returns false
ok &&= f1();
ok &&= f2(); //we may expect f2() is called whatever the f1() returned value

é equivalente a:

bool ok = true;
if (ok) ok = f1();
if (ok) ok = f2(); //f2() is called only when f1() returns true

Este primeiro código é sujeito a erros porque muitos desenvolvedores pensariam f2() é sempre chamado de qualquer que seja o nome f1() valor retornado.É como escrever bool ok = f1() && f2(); onde f2() é chamado apenas quando f1() retorna true.

  • Se o desenvolvedor realmente quiser f2() ser chamado somente quando f1() retorna true, portanto, o segundo código acima é menos sujeito a erros.
  • Caso contrário (o desenvolvedor quer f2() ser sempre chamado), &= é suficiente:

Exemplo para &=

bool ok = true;
ok &= f1();
ok &= f2(); //f2() always called whatever the f1() returned value

Além disso, é mais fácil para o compilador otimizar o código acima do que o código abaixo:

bool ok = true;
if (!f1())  ok = false;
if (!f2())  ok = false;  //f2() always called

Comparar && e &

Podemos perguntar-nos se os operadores && e & dar o mesmo resultado quando aplicado em bool valores?

Vamos verificar usando o seguinte código C++:

#include <iostream>

void test (int testnumber, bool a, bool b)
{
   std::cout << testnumber <<") a="<< a <<" and b="<< b <<"\n"
                "a && b = "<< (a && b)  <<"\n"
                "a &  b = "<< (a &  b)  <<"\n"
                "======================"  "\n";
}

int main ()
{
    test (1, true,  true);
    test (2, true,  false);
    test (3, false, false);
    test (4, false, true);
}

Saída:

1) a=1 and b=1
a && b = 1
a &  b = 1
======================
2) a=1 and b=0
a && b = 0
a &  b = 0
======================
3) a=0 and b=0
a && b = 0
a &  b = 0
======================
4) a=0 and b=1
a && b = 0
a &  b = 0
======================

Conclusão

Portanto SIM podemos substituir && por & para bool valores ;-)
Então é melhor usar &= em vez de &&=.
Podemos considerar &&= tão inútil para booleanos.

O mesmo para ||=

operador |= também é menos sujeito a erros que ||=

Se um desenvolvedor quiser f2() ser chamado somente quando f1() retorna false, em vez de:

bool ok = false;
ok ||= f1();
ok ||= f2(); //f2() is called only when f1() returns false
ok ||= f3(); //f3() is called only when f1() or f2() return false
ok ||= f4(); //f4() is called only when ...

Aconselho a seguinte alternativa mais compreensível:

bool ok = false;
if (!ok) ok = f1();
if (!ok) ok = f2();
if (!ok) ok = f3();
if (!ok) ok = f4();
// no comment required here (code is enough understandable)

ou se preferir tudo em uma linha estilo:

// this comment is required to explain to developers that 
// f2() is called only when f1() returns false, and so on...
bool ok = f1() || f2() || f3() || f4();
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top