Domanda

Esiste qualche motivo per non utilizzare gli operatori bit a bit &, | e ^ per i valori "bool" in C++?

A volte mi imbatto in situazioni in cui voglio che sia vera esattamente una delle due condizioni (XOR), quindi inserisco semplicemente l'operatore ^ in un'espressione condizionale.A volte desidero anche che tutte le parti di una condizione vengano valutate se il risultato è vero o meno (piuttosto che cortocircuitare), quindi utilizzo & e |.A volte ho anche bisogno di accumulare valori booleani e &= e |= possono essere molto utili.

Ho alzato qualche sopracciglio mentre lo facevo, ma il codice è comunque significativo e più pulito di quanto sarebbe altrimenti.C'è qualche motivo per NON usarli per i bool?Esistono compilatori moderni che danno risultati negativi per questo?

È stato utile?

Soluzione

|| E && sono operatori booleani e anche quelli integrati restituiscono sicuramente risultati true O false.Nient'altro.

|, & E ^ sono operatori bit a bit.Quando il dominio dei numeri su cui operi è solo 1 e 0, allora sono esattamente gli stessi, ma nei casi in cui i tuoi booleani non sono strettamente 1 e 0, come nel caso del linguaggio C, potresti ritrovarti con un comportamento non volevi.Ad esempio:

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

In C++, tuttavia, il bool è garantito che il tipo sia solo a true o a false (che si convertono implicitamente rispettivamente in 1 E 0), quindi da questa posizione è meno preoccupante, ma il fatto che le persone non siano abituate a vedere queste cose nel codice costituisce un buon argomento per non farlo.Basta dire b = b && x e farla finita.

Altri suggerimenti

Due ragioni principali.In breve, considera attentamente;potrebbe esserci una buona ragione per questo, ma se c'è, sii MOLTO esplicito nei tuoi commenti perché può essere fragile e, come dici tu stesso, le persone non sono generalmente abituate a vedere un codice come questo.

Xor bit per bit!= Xor logico (tranne 0 e 1)

In primo luogo, se stai operando su valori diversi da false E true (O 0 E 1, come numeri interi), il ^ l'operatore può introdurre un comportamento non equivalente a uno xor logico.Per esempio:

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
}

Ringraziamo l'utente @Patrick per averlo espresso per primo.

Ordine delle operazioni

Secondo, |, &, E ^, come operatori bit a bit, non cortocircuitano.Inoltre, più operatori bit a bit concatenati insieme in una singola istruzione, anche con parentesi esplicite, possono essere riordinati ottimizzando i compilatori, poiché tutte e 3 le operazioni sono normalmente commutative.Questo è importante se l'ordine delle operazioni è importante.

In altre parole

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

non darà sempre lo stesso risultato (o stato finale) di

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

Ciò è particolarmente importante perché potresti non controllare i metodi a() E b(), o qualcun altro potrebbe intervenire e modificarli in seguito senza comprendere la dipendenza e causare un bug sgradevole (e spesso solo di rilascio-build).

Penso

a != b

è quello che vuoi

Le sopracciglia alzate dovrebbero dirti abbastanza per smettere di farlo.Non scrivi il codice per il compilatore, lo scrivi prima per i tuoi colleghi programmatori e poi per il compilatore.Anche se i compilatori funzionano, sorprendere altre persone non è ciò che desideri: gli operatori bit a bit sono per operazioni bit, non per bool.
Suppongo che tu mangi anche le mele con la forchetta?Funziona ma sorprende le persone quindi è meglio non farlo.

Svantaggi degli operatori a livello di bit.

Tu chiedi:

“C'è qualche motivo per non utilizzare gli operatori bit a bit &, |, E ^ per i valori "bool" in C++?"

Sì, il operatori logici, ovvero gli operatori booleani di alto livello integrati !, && E ||, offrono i seguenti vantaggi:

  • Garantita conversione degli argomenti A bool, cioè.A 0 E 1 valore ordinale.

  • Garantita valutazione del cortocircuito dove la valutazione dell'espressione si interrompe non appena si conosce il risultato finale.
    Questo può essere interpretato come una logica del valore dell'albero, con VERO, Falso E Indeterminato.

  • Equivalenti testuali leggibili not, and E or, anche se non li uso personalmente.
    Come nota il lettore Antimony in un commento, anche gli operatori a livello di bit hanno token alternativi, vale a dire bitand, bitor, xor E compl, ma secondo me sono meno leggibili di and, or E not.

In poche parole, ciascuno di questi vantaggi degli operatori di alto livello è uno svantaggio degli operatori di livello bit.

In particolare, poiché gli operatori bit a bit non hanno la conversione degli argomenti in 0/1, ottieni ad es. 1 & 20, Mentre 1 && 2true.Anche ^, esclusivo bit per bit o, può comportarsi in modo anomalo in questo modo.Considerati come valori booleani 1 e 2 sono gli stessi, vale a dire true, ma considerati bitpattern sono diversi.


Come esprimere la logica o/o nel C++.

Quindi fornisci un po' di contesto per la domanda,

"A volte mi imbatto in situazioni in cui voglio che sia vera esattamente una delle due condizioni (XOR), quindi inserisco semplicemente l'operatore ^ in un'espressione condizionale."

Bene, gli operatori bit a bit lo hanno fatto precedenza più elevata rispetto agli operatori logici.Ciò significa in particolare che in un'espressione mista come

a && b ^ c

ottieni il risultato forse inaspettato a && (b ^ c).

Invece scrivi semplicemente

(a && b) != c

esprimendo in modo più conciso ciò che intendi.

Per l'argomento multiplo o/o non esiste un operatore C++ che faccia il lavoro.Ad esempio, se scrivi a ^ b ^ c di quella non è un'espressione che dice "neanche". a, b O c è vero".Invece dice: “Un numero dispari di a, b E c sono vere", che potrebbe essere 1 di esse o tutte e 3...

Per esprimere il generale aut-aut quando a, b E c sono di tipo bool, Scrivi e basta

(a + b + c) == 1

o, con non-bool argomenti, convertirli in bool:

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


Utilizzando &= per accumulare risultati booleani.

Elaborerai ulteriormente,

“A volte ho anche bisogno di accumulare valori booleani e &= E |=? può essere molto utile."

Bene, questo corrisponde a verificare se rispettivamente Tutto O Qualunque la condizione è soddisfatta e legge di de Morgan ti dice come passare dall'uno all'altro.Cioè.te ne serve solo uno.In linea di principio potresti usare *= come un &&=-operator (come scoprì il buon vecchio George Boole, l'AND logico può essere facilmente espresso come moltiplicazione), ma penso che ciò lascerebbe perplessi e forse fuorviarebbe i manutentori del codice.

Considera anche:

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;
}

Output con Visual C++ 11.0 e g++ 4.7.1:

true
false

Il motivo della differenza nei risultati è che il bitlevel &= non fornisce una conversione a bool del suo argomento di destra.

Quindi, quale di questi risultati desideri utilizzare &=?

Se il primo, true, quindi definire meglio un operatore (ad es.come sopra) o una funzione denominata, oppure utilizzare una conversione esplicita dell'espressione a destra, oppure scrivere l'aggiornamento per intero.

Contrariamente alla risposta di Patrick, il C++ non ha ^^ operatore per eseguire un cortocircuito esclusivo o.Se ci pensi per un secondo, avere a ^^ l'operatore non avrebbe comunque senso:con l'or esclusivo il risultato dipende sempre da entrambi gli operandi.Tuttavia, l'avvertimento di Patrick riguardo al non-bool I tipi "booleani" reggono altrettanto bene durante il confronto 1 & 2 A 1 && 2.Un classico esempio di ciò è Windows GetMessage() funzione, che restituisce un tristato BOOL:diverso da zero, 0, O -1.

Utilizzando & invece di && E | invece di || non è un errore di battitura insolito, quindi se lo fai deliberatamente, merita un commento che ne spieghi il motivo.

Patrick ha espresso ottimi punti e non li ripeterò.Tuttavia, potrei suggerire di ridurre le istruzioni "if" in un inglese leggibile, ove possibile, utilizzando variabili booleane con nomi ben definiti. Ad esempio, e questo utilizza operatori booleani, ma potresti anche utilizzare bit per bit e denominare i valori booleani in modo appropriato:

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

Potresti pensare che l'utilizzo di un valore booleano sembri non necessario, ma aiuta con due cose principali:

  • Il tuo codice è più facile da capire perché il valore booleano intermedio per la condizione "se" rende più esplicita l'intenzione della condizione.
  • Se stai utilizzando codice non standard o imprevisto, come operatori bit a bit su valori booleani, le persone potranno capire molto più facilmente perché lo hai fatto.

MODIFICARE:Non hai detto esplicitamente che volevi i condizionali per le affermazioni "se" (anche se questo sembra più probabile), questa era la mia ipotesi.Ma il mio suggerimento di un valore booleano intermedio è ancora valido.

IIRC, molti compilatori C++ avviseranno quando tentano di convertire il risultato di un'operazione bit a bit come bool.Dovresti usare un cast di tipo per rendere felice il compilatore.

Usare un'operazione bit a bit in un'espressione if potrebbe servire alla stessa critica, anche se forse non da parte del compilatore.Qualsiasi valore diverso da zero è considerato vero, quindi qualcosa come "se (7 e 3)" sarà vero.Questo comportamento può essere accettabile in Perl, ma C/C++ sono linguaggi molto espliciti.Penso che il sopracciglio di Spock sia dovuto alla dovuta diligenza.:) Aggiungerei "== 0" o "!= 0" per rendere perfettamente chiaro quale fosse il tuo obiettivo.

Ma comunque, sembra una preferenza personale.Vorrei eseguire il codice tramite lint o uno strumento simile e vedere se pensa anche che sia una strategia poco saggia.Personalmente, sembra un errore di codifica.

L'uso di operazioni bit a bit per bool aiuta a salvare la logica di previsione dei rami non necessaria da parte del processore, risultante da un'istruzione "cmp" introdotta dalle operazioni logiche.

Sostituendo la logica con operazioni bit a bit (dove tutti gli operandi sono bool) si genera un codice più efficiente che offre lo stesso risultato.Idealmente, l'efficienza dovrebbe superare tutti i vantaggi del cortocircuito che possono essere sfruttati nell'ordinamento utilizzando operazioni logiche.

Ciò può rendere il codice un po' illeggibile, anche se il programmatore dovrebbe commentarlo con le ragioni per cui è stato fatto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top