Pergunta

Existe alguma razão para não usar os operadores bit a bit &, | e ^ para valores "bool" em C++?

Às vezes me deparo com situações em que desejo que exatamente uma das duas condições seja verdadeira (XOR), então apenas coloco o operador ^ em uma expressão condicional.Às vezes, também quero que todas as partes de uma condição sejam avaliadas, independentemente de o resultado ser verdadeiro ou não (em vez de curto-circuito), então uso & e |.Às vezes, também preciso acumular valores booleanos, e &= e |= podem ser bastante úteis.

Fiquei com algumas sobrancelhas levantadas ao fazer isso, mas o código ainda é significativo e mais limpo do que seria de outra forma.Existe alguma razão para NÃO usá-los para bools?Existem compiladores modernos que dão resultados ruins para isso?

Foi útil?

Solução

|| e && são operadores booleanos e os integrados têm garantia de retornar true ou false.Nada mais.

|, & e ^ são operadores bit a bit.Quando o domínio de números em que você opera é apenas 1 e 0, então eles são exatamente iguais, mas nos casos em que seus booleanos não são estritamente 1 e 0 – como é o caso da linguagem C – você pode acabar com algum comportamento você não queria.Por exemplo:

BOOL two = 2;
BOOL one = 1;
BOOL and = two & one;   //and = 0
BOOL cand = two && one; //cand = 1

Em C++, no entanto, o bool tipo é garantido como sendo apenas um true ou um false (que convertem implicitamente para respectivamente 1 e 0), portanto, essa postura é menos preocupante, mas o fato de as pessoas não estarem acostumadas a ver essas coisas no código é um bom argumento para não fazê-lo.Apenas diga b = b && x e acabar com isso.

Outras dicas

Duas razões principais.Resumindo, considere cuidadosamente;pode haver uma boa razão para isso, mas se houver MUITO explícito em seus comentários, porque pode ser frágil e, como você mesmo diz, as pessoas geralmente não estão acostumadas a ver códigos como este.

Xor bit a bit! = Xor lógico (exceto 0 e 1)

Em primeiro lugar, se você estiver operando com valores diferentes false e true (ou 0 e 1, como números inteiros), o ^ operador pode introduzir comportamento não equivalente a um xor lógico.Por exemplo:

int one = 1;
int two = 2;

// bitwise xor
if (one ^ two)
{
  // executes because expression = 3 and any non-zero integer evaluates to true
}

// logical xor; more correctly would be coded as
//   if (bool(one) != bool(two))
// but spelled out to be explicit in the context of the problem
if ((one && !two) || (!one && two))
{
  // does not execute b/c expression = ((true && false) || (false && true))
  // which evaluates to false
}

Agradecemos ao usuário @Patrick por expressar isso primeiro.

Ordem de operações

Segundo, |, &, e ^, como operadores bit a bit, não causam curto-circuito.Além disso, vários operadores bit a bit encadeados em uma única instrução - mesmo com parênteses explícitos - podem ser reordenados otimizando os compiladores, porque todas as três operações são normalmente comutativas.Isto é importante se a ordem das operações for importante.

Em outras palavras

bool result = true;
result = result && a() && b();
// will not call a() if result false, will not call b() if result or a() false

nem sempre dará o mesmo resultado (ou estado final) que

bool result = true;
result &= (a() & b());
// a() and b() both will be called, but not necessarily in that order in an
// optimizing compiler

Isto é especialmente importante porque você não pode controlar os métodos a() e b(), ou alguém pode aparecer e alterá-los mais tarde, sem entender a dependência, e causar um bug desagradável (e muitas vezes apenas de compilação de lançamento).

Eu penso

a != b

é o que você quer

As sobrancelhas levantadas devem dizer o suficiente para você parar de fazer isso.Você não escreve o código para o compilador, você o escreve primeiro para seus colegas programadores e depois para o compilador.Mesmo que os compiladores funcionem, surpreender outras pessoas não é o que você deseja - os operadores bit a bit são para operações bit, não para bools.
Suponho que você também coma maçãs com garfo?Funciona, mas surpreende as pessoas, então é melhor não fazer isso.

Desvantagens dos operadores de nível de bit.

Você pergunta:

“Existe alguma razão para não usar os operadores bit a bit &, |, e ^ para valores "bool" em C++?”

Sim o Operadores lógicos, esses são os operadores booleanos integrados de alto nível !, && e ||, oferecem as seguintes vantagens:

  • Garantido conversão de argumentos para bool, ou sejapara 0 e 1 valor ordinal.

  • Garantido avaliação de curto-circuito onde a avaliação da expressão para assim que o resultado final é conhecido.
    Isso pode ser interpretado como uma lógica de valor em árvore, com Verdadeiro, Falso e Indeterminado.

  • Equivalentes textuais legíveis not, and e or, mesmo que eu não os use sozinho.
    Como o leitor Antimony observa em um comentário, os operadores de nível de bit também possuem tokens alternativos, a saber bitand, bitor, xor e compl, mas na minha opinião estes são menos legíveis do que and, or e not.

Simplificando, cada vantagem dos operadores de alto nível é uma desvantagem dos operadores de nível de bit.

Em particular, como os operadores bit a bit não possuem conversão de argumento para 0/1, você obtém, por exemplo. 1 & 20, enquanto 1 && 2true.Também ^, exclusivo bit a bit ou, pode se comportar mal dessa maneira.Considerados como valores booleanos 1 e 2 são iguais, ou seja, true, mas considerados padrões de bits, eles são diferentes.


Como expressar lógico ou em C++.

Em seguida, você fornece um pouco de base para a pergunta,

“Às vezes me deparo com situações em que quero que exatamente uma das duas condições seja verdadeira (XOR), então apenas coloco o operador ^ em uma expressão condicional.”

Bem, os operadores bit a bit têm maior precedência do que os operadores lógicos.Isto significa, em particular, que numa expressão mista como

a && b ^ c

você obtém o resultado talvez inesperado a && (b ^ c).

Em vez disso, escreva apenas

(a && b) != c

expressando de forma mais concisa o que você quer dizer.

Para o argumento múltiplo ou não há operador C++ que faça o trabalho.Por exemplo, se você escrever a ^ b ^ c do que isso não é uma expressão que diz “ou a, b ou c é verdade".Em vez disso, diz: “Um número ímpar de a, b e c são verdadeiros“, que pode ser 1 deles ou todos os 3…

Para expressar o geral ou/ou quando a, b e c são do tipo bool, apenas escreva

(a + b + c) == 1

ou, com nãobool argumentos, converta-os em bool:

(!!a + !!b + !!c) == 1


Usando &= para acumular resultados booleanos.

Você elabora ainda mais,

“Às vezes também preciso acumular valores booleanos, e &= e |=? pode ser bastante útil.”

Bem, isso corresponde a verificar se respectivamente todos ou qualquer condição é satisfeita, e lei de Morgan diz como passar de um para o outro.Ou sejavocê só precisa de um deles.Você poderia, em princípio, usar *= como um &&=-operador (pois como o bom e velho George Boole descobriu, o AND lógico pode muito facilmente ser expresso como multiplicação), mas acho que isso deixaria perplexos e talvez induziria em erro os mantenedores do código.

Considere também:

struct Bool
{
    bool    value;

    void operator&=( bool const v ) { value = value && v; }
    operator bool() const { return value; }
};

#include <iostream>

int main()
{
    using namespace std;

    Bool a  = {true};
    a &= true || false;
    a &= 1234;
    cout << boolalpha << a << endl;

    bool b = {true};
    b &= true || false;
    b &= 1234;
    cout << boolalpha << b << endl;
}

Saída com Visual C++ 11.0 e g++ 4.7.1:

true
false

A razão para a diferença nos resultados é que o nível de bits &= não fornece uma conversão para bool de seu argumento do lado direito.

Então, qual desses resultados você deseja para o uso do &=?

Se o primeiro, true, então defina melhor um operador (por exemplocomo acima) ou função nomeada, ou use uma conversão explícita da expressão do lado direito, ou escreva a atualização completa.

Ao contrário da resposta de Patrick, C++ não tem ^^ operador para realizar um curto-circuito exclusivo ou.Se você pensar sobre isso por um segundo, ter um ^^ operador não faria sentido de qualquer maneira:com exclusivo ou, o resultado sempre depende de ambos os operandos.No entanto, o alerta de Patrick sobre a não-bool Os tipos "booleanos" funcionam igualmente bem quando comparados 1 & 2 para 1 && 2.Um exemplo clássico disso é o Windows GetMessage() função, que retorna um estado triplo BOOL:diferente de zero, 0, ou -1.

Usando & em vez de && e | em vez de || não é um erro de digitação incomum; portanto, se você estiver fazendo isso deliberadamente, merece um comentário dizendo o porquê.

Patrick fez bons comentários e não vou repeti-los.No entanto, posso sugerir reduzir as instruções 'if' para um inglês legível sempre que possível, usando vars booleanos bem nomeados. Por exemplo, e isso está usando operadores booleanos, mas você também pode usar bit a bit e nomear os booleanos apropriadamente:

bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here
bool onlyBIsTrue = (b && !a); // and not need this second line
if (onlyAIsTrue || onlyBIsTrue)
{
 .. stuff ..
}

Você pode pensar que usar um booleano parece desnecessário, mas ajuda em duas coisas principais:

  • Seu código é mais fácil de entender porque o booleano intermediário da condição 'if' torna a intenção da condição mais explícita.
  • Se você estiver usando código não padrão ou inesperado, como operadores bit a bit em valores booleanos, as pessoas poderão ver muito mais facilmente por que você fez isso.

EDITAR:Você não disse explicitamente que queria as condicionais para declarações 'se' (embora isso pareça mais provável), essa foi a minha suposição.Mas minha sugestão de um valor booleano intermediário ainda permanece.

IIRC, muitos compiladores C++ avisarão ao tentar converter o resultado de uma operação bit a bit como um bool.Você teria que usar uma conversão de tipo para deixar o compilador feliz.

Usar uma operação bit a bit em uma expressão if serviria à mesma crítica, embora talvez não por parte do compilador.Qualquer valor diferente de zero é considerado verdadeiro, então algo como "se (7 e 3)" será verdadeiro.Este comportamento pode ser aceitável em Perl, mas C/C++ são linguagens muito explícitas.Acho que a sobrancelha de Spock é uma questão de diligência.:) Eu acrescentaria "== 0" ou "!= 0" para deixar perfeitamente claro qual era o seu objetivo.

Mas de qualquer forma, parece uma preferência pessoal.Eu executaria o código por meio de lint ou ferramenta semelhante e veria se ele também acha que é uma estratégia imprudente.Pessoalmente, parece um erro de codificação.

O uso de operações bit a bit para bool ajuda a economizar lógica de previsão de ramificação desnecessária pelo processador, resultante de uma instrução 'cmp' trazida por operações lógicas.

Substituir as operações lógicas por operações bit a bit (onde todos os operandos são bool) gera um código mais eficiente que oferece o mesmo resultado.Idealmente, a eficiência deve superar todos os benefícios de curto-circuito que podem ser aproveitados no pedido usando operações lógicas.

Isso pode tornar o código um pouco ilegível, embora o programador deva comentá-lo explicando as razões pelas quais isso foi feito.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top