Perché usare !! quando si converte int in bool?

Quale può essere una ragione per convertire un intero in un booleano in questo modo?

bool booleanValue = !!integerValue; 

invece di solo

 bool booleanValue = integerValue; 

Tutto quello che so è che in VC ++ 7 quest’ultimo causerà l’allarme C4800 e il primo no. C’è qualche altra differenza tra i due?

I problemi con il “!!” l’idioma è che è conciso, difficile da vedere, facile da confondere per un errore di battitura, facile da rilasciare uno dei “!” s “, e così via. L’ho inserito nella categoria “guarda come possiamo essere simpatici con C / C ++”.

Basta scrivere bool isNonZero = (integerValue != 0); … essere chiaro.

Storicamente, il !! l’idioma è stato usato per assicurare che il tuo bool contenesse veramente uno dei due valori previsti in una variabile bool -like, perché C e C ++ non avevano un vero tipo bool e l’abbiamo simulato con int s. Questo è meno di un problema ora con “veri” bool s.

Ma usando !! è un mezzo efficace per documentare (sia per il compilatore che per le persone future che lavorano nel tuo codice) che sì, hai davvero intenzione di lanciare quella int a un bool .

È usato perché il linguaggio C (e anche alcuni compilatori C ++ pre-standard) non avevano il tipo bool , solo int . Quindi gli int erano usati per rappresentare i valori logici: 0 doveva significare false , e tutto il resto era true . Il ! l’operatore stava restituendo 1 da 0 e 0 da tutto il resto. Doppio ! è stato usato per invertire quelli, ed era lì per assicurarsi che il valore fosse solo 0 o 1 seconda del suo valore logico.

In C ++, dall’introduzione di un tipo bool corretto, non è più necessario farlo. Ma non puoi semplicemente aggiornare tutte le fonti legacy e non dovresti, a causa della retrocompatibilità di C con C ++ (il più delle volte). Ma molte persone lo fanno ancora, per lo stesso motivo: mantenere il loro codice compatibile con i vecchi compilatori che ancora non capiscono i bool .

E questa è l’unica vera risposta. Altre risposte sono fuorvianti.

Perché! IntegerValue significa integerValue == 0 e !! integerValue significa integerValue! = 0, un’espressione valida che restituisce un valore bool. Quest’ultimo è un cast con perdita di informazioni.

Un’altra opzione è l’operatore ternario che sembra generare una riga in meno del codice assembly (comunque in Visual Studio 2005):

 bool ternary_test = ( int_val == 0 ) ? false : true; 

che produce il codice assembly:

 cmp DWORD PTR _int_val$[ebp], 0 setne al mov BYTE PTR _ternary_test$[ebp], al 

Contro:

 bool not_equal_test = ( int_val != 0 ); 

che produce:

 xor eax, eax cmp DWORD PTR _int_val$[ebp], 0 setne al mov BYTE PTR _not_equal_test$[ebp], al 

So che non è un’enorme differenza, ma ero curioso e ho pensato di condividere le mie scoperte.

Un bool può avere solo due stati, 0 e 1. Un numero intero può avere qualsiasi stato da -2147483648 a 2147483647 assumendo un numero intero a 32 bit con segno. L’unario! le uscite dell’operatore 1 se l’ingresso è 0 e le uscite 0 se l’ingresso è qualsiasi cosa eccetto 0. So! 0 = 1 e! 234 = 0. Il secondo! cambia semplicemente l’uscita in modo che 0 diventi 1 e 1 diventi 0.

Quindi la prima affermazione garantisce che booleanValue sarà impostato uguale a 0 o 1 e nessun altro valore, la seconda non lo farà.

!! è un modo idiomatico per convertire in bool , e funziona per bloccare il sillywarning del compilatore di Visual C ++ sulla presunta inefficienza di tale conversione.

Vedo dalle altre risposte e commenti che molte persone non hanno familiarità con l’utilità di questo idioma nella programmazione di Windows. Il che significa che non hanno fatto alcuna programmazione seria di Windows. E supponiamo ciecamente che ciò che hanno incontrato sia rappresentativo (non lo è).

 #include  using namespace std; int main( int argc, char* argv[] ) { bool const b = static_cast< bool >( argc ); (void) argv; (void) b; } 
 > [d: \ dev \ test]
 > cl foo.cpp
 foo.cpp
 foo.cpp (6): avviso C4800: 'int': forzare il valore in bool 'true' o 'false' (avviso prestazioni)

 [D: \ dev \ test]
 > _

E almeno una persona pensa che se un novizio inesperto non ne riconosce il significato, allora non è buono. Bene, è stupido. C’è molto che i novizi inesperti non riconoscono o capiscono. Scrivere il proprio codice in modo che possa essere compreso da qualsiasi novizio assoluto non è qualcosa per i professionisti. Neanche per gli studenti. A partire dal percorso di esclusione degli operatori e delle combinazioni di operatori che i novizi inesperti non riconoscono … Beh, non ho le parole per dare a quell’approccio una descrizione appropriata, mi dispiace.

Saluti e hth.,

La risposta di user143506 è corretta ma per un ansible problema di prestazioni ho confrontato le possibilità di asm:

return x; , return x != 0; , return !!x; e anche return boolean_cast(x) risulta in questo set perfetto di istruzioni asm:

 test edi/ecx, edi/ecx setne al ret 

Questo è stato testato per GCC 7.1 e MSVC 19 2017. (Solo il boolean_converter in MSVC 19 2017 si traduce in una quantità maggiore di codice asm ma ciò è causato dalla templatizzazione e dalle strutture e può essere trascurato dal punto di vista delle prestazioni, perché lo stesso le linee come indicato sopra possono semplicemente essere duplicate per funzioni diverse con lo stesso runtime.)

Questo significa: non c’è differenza di prestazioni.

PS: questo boolean_cast è stato utilizzato:

 #define BOOL int // primary template template< class TargetT, class SourceT > struct boolean_converter; // full specialization template< > struct boolean_converter { static bool convert(BOOL b) { return b ? true : false; } }; // Type your code here, or load an example. template< class TargetT, class SourceT > TargetT boolean_cast(SourceT b) { typedef boolean_converter converter_t; return converter_t::convert(b); } bool is_non_zero(int x) { return boolean_cast< bool >(x); } 

Nessun grande motivo tranne essere paranoico o urlare attraverso il codice che è un bool.

per il compilatore, alla fine non farà differenza.

Non mi è mai piaciuta questa tecnica di conversione in un tipo di dati bool : ha un cattivo odore!

Invece, stiamo usando un modello pratico chiamato boolean_cast trovato qui . È una soluzione flessibile che è più esplicita in ciò che sta facendo e può essere utilizzata come segue:

 bool IsWindow = boolean_cast< bool >(::IsWindow(hWnd));