Por que o c ++ não possui && = ou || = para booleanos?

126

Existe uma "coisa muito ruim" que pode acontecer &&=e ||=foi usada como açúcar sintático para bool foo = foo && bare bool foo = foo || bar?

Kache
fonte
5
Veja esta outra pergunta: stackoverflow.com/questions/2324549/… Essa é sobre Java, mas compartilhando a linhagem C, os mesmos argumentos se aplicam principalmente.
jamesdlin
Basicamente, apenas o c ++ não tem b / c que não o colocaram - linguagens como o Ruby. boo ...
Kache
2
Mas em Ruby, não é x ||= yaproximadamente equivalente a C ++ x = x ? x : y;para qualquer tipo? Em outras palavras, "defina como y se ainda não estiver definido". Isso é consideravelmente mais útil do que C ou C ++ x ||= y, que (exceto a sobrecarga do operador) faria "definir x como a (bool)ymenos que já esteja definido". Não estou ansioso para adicionar outro operador para isso, parece um pouco fraco. Apenas escreva if (!x) x = (bool)y. Mas, na verdade, não uso boolvariáveis ​​suficientes para desejar operadores extras que sejam realmente úteis apenas com esse tipo.
precisa
1
Tenho certeza que o principal motivo pelo qual o C ++ não possui &&=ou ||=é simplesmente porque o C não os possui. Tenho certeza de que a razão pela qual C não os possui é que a funcionalidade não foi considerada benéfica o suficiente.
Jonathan Leffler
2
Além disso, sendo ultra-pedante, a notação bool foo = foo || bar;invocaria um comportamento indefinido porque foonão é inicializado antes da avaliação de foo || bar. Obviamente, isso pretende ser algo parecido bool foo = …initialization…; …; foo = foo || bar;e a pergunta permanece válida.
Jonathan Leffler

Respostas:

73

A boolpode ser apenas trueou falseem C ++. Como tal, usar &=e |=é relativamente seguro (mesmo que eu não goste da notação). É verdade que eles executarão operações de bits em vez de operações lógicas (e, portanto, não entrarão em curto-circuito), mas essas operações de bits seguem um mapeamento bem definido, que é efetivamente equivalente às operações lógicas, desde que os dois operandos sejam do tipobool . 1

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

A única maneira de obter um valor inválido em a boolé usando reinterpret_castponteiros:

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

Porém, como esse código resulta em um comportamento indefinido, podemos ignorar com segurança esse possível problema em conformidade com o código C ++.


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

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

O motivo é que b & 2realiza a promoção inteira, de forma que a expressão seja equivalente a static_cast<int>(b) & 2, o que resulta em 0, e depois é convertida novamente em a bool. Portanto, é verdade que a existência de um operator &&=melhoraria a segurança do tipo.

Konrad Rudolph
fonte
4
Mas o && e || operadores trabalharão em qualquer coisa que seja convertida em bool, não apenas bool.
precisa saber é o seguinte
13
Eles não fazem a mesma coisa, mesmo em bools. ||e &&atalho, ou seja, o segundo argumento não é operando se o primeiro operando for true(resp. falsefor &&). |, &, |=E &=sempre avaliar ambos os operandos.
Niki
4
isso não responde à pergunta de por que && = e || = não são operadores de c ++.
thang
9
É não seguro para uso &=por um lado esquerdo do tipo bool, porque é perfeitamente possível para o lado direito de ser do tipo que não seja bool(como islowerou outra função stdlib C que retorna diferente de zero para valor verdadeiro). Se tivéssemos o hipotético &&=, provavelmente forçaria o lado direito a se converter bool, o que &=não acontece. Em outras palavras, bool b = true; b &= 2;resulta em b == false.
Angew não está mais orgulhoso de SO
2
@Antonio Tough multidão. Kon
Konrad Rudolph
44

&&e &tem semântica diferente: &&não avaliará o segundo operando se o primeiro operando for false. ou seja, algo como

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

está seguro, mas

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

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

O mesmo vale para &=e |=:

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

se comportará de maneira diferente de:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();
Niki
fonte
35
mais um motivo para ter && = na minha opinião. = P
Kache 21/03
4
Esta não é realmente uma resposta, no entanto.
Catskul
27

Resposta curta

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

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

No entanto, os operadores &&=e ||=seriam lógicos, e esses operadores podem estar sujeitos a erros, porque muitos desenvolvedores esperam que foo()sejam sempre chamados 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
  • Nós realmente precisamos tornar o C / C ++ ainda mais complexo para obter um atalho x = x && foo()?

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


Resposta longa

Exemplo para &&=

Se o &&=operador estivesse disponível, 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

Esse primeiro código é suscetível a erros porque muitos desenvolvedores pensam que f2()sempre é chamado de qualquer 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 apenas quando f1()retornartrue , o segundo código acima é menos suscetível a erros.
  • Senão (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 esse código acima do que o código abaixo:

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

Compare &&e&

Podemos nos perguntar se os operadores &&e &obter o mesmo resultado quando aplicados embool 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);
}

Resultado:

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 &em boolvalores ;-)
Então é melhor usar &=em vez de &&=.
Podemos considerar&&= inútil para os booleanos.

O mesmo para ||=

operador |=também é menos propenso a erros do que||=

Se um desenvolvedor quiser f2()ser chamado apenas quando f1()retornar 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 você preferir tudo em um estilo de linha :

// 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();
olibre
fonte
13
E se eu realmente quiser esse comportamento? Que a expressão da mão direita não é executada se a expressão da mão esquerda estiver incorreta. É chato escrever as variáveis ​​duas vezes, comosuccess = success && DoImportantStuff()
Niklas R
1
Meu conselho é escrever if(success) success = DoImportantStuff(). Se a declaração success &&= DoImportantStuff()fosse permitida, muitos desenvolvedores pensariam que DoImportantStuff()sempre é chamado qualquer que seja o valor success. Espero que isso responda o que você quer saber ... Também melhorei muitas partes da minha resposta. Por favor, diga-me se minha resposta é mais compreensível agora? (sobre o seu comentário propósito) Cheers, Vê-lo ;-)
olibre
9
"Se a declaração success &&= DoImportantStuff()fosse permitida, muitos desenvolvedores pensariam que DoImportantStuff()sempre é chamado de qualquer valor do sucesso". Você pode dizer isso sobre isso if (success && DoImportantStuff()). Desde que se lembrem da lógica por trás da sintaxe if, eles não devem ter problemas &&=.
pilkch
2
Não vejo como as pessoas podem assumir que f1()sempre avalia, ok &&= f(1) mas não presumem que sempre avalia ok = ok && f(1). Parece tão provável para mim.
einpoklum
1
Na verdade, eu espero v1 += e2ser o equivalente sintático do açúcar da v1 = v1 + e1variável v1 e da expressão e2. Apenas uma notação abreviada, só isso.
einpoklum