Warum verwenden !! beim konvertieren von int in bool?

76

Was kann ein Grund sein, eine Ganzzahl auf diese Weise in einen Booleschen Wert umzuwandeln?

bool booleanValue = !!integerValue;

statt nur

bool booleanValue = integerValue;

Ich weiß nur, dass in VC ++ 7 Letzteres eine C4800-Warnung verursacht und Ersteres nicht. Gibt es einen anderen Unterschied zwischen den beiden?

scharfer Zahn
quelle
2
Nach der doppelten Negation ist der Wert garantiert 0 oder 1; Der ursprüngliche Wert kann ein beliebiger int-Wert sein.
Martin v. Löwis
1
Aber warum wird die zweite Aussage nicht genau dasselbe tun?
Scharfzahn
2
Ich denke, die Frage ist, warum Ersteres auch keinen C4800 verursacht, da selbst "Das Umwandeln des Ausdrucks in Typ Bool die Warnung nicht deaktiviert, was beabsichtigt ist." (MS)
Tobias
2
Warum ahnen nicht mehr Menschen, dass dies die richtige Antwort ist ? doppelt negativ ist sehr häufig in Low-Level-Code
Matt Joiner
ähm, das hat tatsächlich seinen Platz ... die Leute verwirren C ++ und C
Matt Joiner

Antworten:

112

Die Probleme mit dem "!!" Redewendung ist, dass es knapp, schwer zu sehen, leicht mit einem Tippfehler zu verwechseln, leicht eines der "!" zu löschen und so weiter. Ich habe es in die Kategorie "Schau, wie süß wir mit C / C ++ sein können" eingeordnet.

Schreiben Sie einfach bool isNonZero = (integerValue != 0);... seien Sie klar.

user143506
quelle
11
+1 für "bool isNonZero = (integerValue! = 0);". Ich hasse es immer, wenn Code Ints, Doubles, Ptrs usw. mit Bools verbindet. Sie sind verschiedene Arten und sollten als solche behandelt werden. Die implizite Umwandlung eines Bools in einen numerischen Typ ist ein Anchronismus.
Jon-Hanson
8
Ich sehe kein Problem darin, Ints / Zeiger als Bools zu behandeln - es ist eine Standard-C / C ++ - Sprache, und von jedem Entwickler, der sie beherrscht, sollte erwartet werden, dass er sie kennt. Manchmal möchten Sie jedoch Compiler-Warnungen und dergleichen vermeiden - und in diesen Fällen ist ein expliziter Vergleich (oder eine Besetzung) dem relativ obskuren !!Trick definitiv vorzuziehen .
Pavel Minaev
3
0 ist gleich falsch, weil der Standard dies sagt ;-) !! ist eine ziemlich verbreitete Redewendung, mit der Sie einfach nicht vertraut zu sein scheinen. Das macht es nicht unbedingt schlecht, außer für die Verwendung am Anfang eines Satzes in natürlicher Sprache.
Gunther Piez
16
+1 für "Schau, wie süß wir mit C / C ++ sein können". Fasst so viele schlechte Codierungspraktiken perfekt zusammen, dass die C / C ++ - Syntax und lose Regeln dies ermöglichen.
David
8
-1 für "bool isNonZero = (integerValue! = 0);" ist dies weder lesbarer noch "intuitiver". Wenn Sie keine Standard-C / C ++ - Redewendungen wie !! mögen, verwenden Sie eine andere Sprache.
Gunther Piez
49

In der !!Vergangenheit wurde das Idiom verwendet, um sicherzustellen, dass Ihr Bool tatsächlich einen der beiden Werte enthält, die in einer boolähnlichen Variablen erwartet werden, da C und C ++ keinen echten boolTyp hatten und wir ihn mit ints gefälscht haben . Dies ist jetzt bei "echten" weniger ein Problem bool.

Aber der Einsatz !!ist ein effizientes Mittel zur Dokumentation (sowohl für den Compiler und alle zukünftigen Menschen in Ihrem Code arbeiten) , dass ja, Sie wirklich Guss die Absicht hatte , dass intzu ein bool.

TJ Crowder
quelle
10
Ich würde nicht "ein effizientes Mittel zur Dokumentation" sagen, wenn das OP nicht einmal verstehen würde, was es bedeutet. Ein viel besserer Weg wäre static_cast <bool> (integerValue).
Arolson101
10
Das OP wusste wahrscheinlich zuerst nicht, was es List<String>bedeutete, oder ||=. !!ist eine sehr, sehr tief verwurzelte Sprache im C- und C ++ - Universum. Wenn jemand es nicht weiß, sollte er es lernen - und dann seine eigene begründete Einschätzung darüber abgeben, ob er es verwenden soll.
TJ Crowder
@ Arolson, @ TJ Crowder: !! mag effizient sein, aber es ist unwirksam
Lie Ryan
Kann ein "echter" Bool mehr als zwei Werte haben? Was ist zum Beispiel das Ergebnis von (bool) 1 ^ (bool) 2 Ist es 0 oder 3?
Dspyz
1
@ arolson101: static_cast <bool> (integerValue) ist eine Warnung in VSCPP 2015 (v14). Wenn Warnungen Fehler für Sie (oder für Ihren Code-Prüfer) sind, haben Sie
kein
14

Es wird verwendet, weil die C-Sprache (und einige vorstandardisierte C ++ - Compiler auch) nicht den boolTyp hatte, nur int. Also wurden die ints verwendet, um logische Werte darzustellen: 0sollte bedeuten false, und alles andere war true. Der !Betreiber kehrte 1von 0und zu 0allem anderen zurück. Double !wurde verwendet, um diese zu invertieren, und es wurde verwendet, um sicherzustellen, dass der Wert gerecht ist 0oder 1von seinem logischen Wert abhängt.

In C ++ ist dies seit Einführung eines geeigneten boolTyps nicht mehr erforderlich. Aufgrund der Abwärtskompatibilität von C mit C ++ (meistens) können Sie jedoch nicht alle älteren Quellen aktualisieren und sollten dies auch nicht müssen. Aber viele Leute tun es immer noch aus demselben Grund: um ihren Code abwärtskompatibel mit alten Compilern zu bleiben, die bools immer noch nicht verstehen .

Und das ist die einzig wahre Antwort. Andere Antworten sind irreführend.

SasQ
quelle
13

Weil! IntegerValue integerValue == 0 bedeutet und !! integerValue also integerValue! = 0 bedeutet, ein gültiger Ausdruck, der einen Bool zurückgibt. Letzteres ist eine Besetzung mit Informationsverlust.

StampedeXV
quelle
7

Eine weitere Option ist der ternäre Operator, der scheinbar eine Zeile weniger Assembler-Code generiert (in Visual Studio 2005 jedenfalls):

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

welches den Assembler-Code erzeugt:

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

Gegen:

bool not_equal_test = ( int_val != 0 );

welches produziert:

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

Ich weiß, dass es kein großer Unterschied ist, aber ich war neugierig und dachte nur, dass ich meine Erkenntnisse teilen würde.

Brian
quelle
5

Ein Bool kann nur zwei Zustände haben, 0 und 1. Eine Ganzzahl kann einen beliebigen Zustand von -2147483648 bis 2147483647 haben, wobei eine vorzeichenbehaftete 32-Bit-Ganzzahl angenommen wird. Das Unäre! Der Operator gibt 1 aus, wenn der Eingang 0 ist, und gibt 0 aus, wenn der Eingang etwas anderes als 0 ist. Also! 0 = 1 und! 234 = 0. Der zweite! schaltet einfach den Ausgang so, dass 0 zu 1 und 1 zu 0 wird.

Die erste Anweisung garantiert also, dass booleanValue entweder auf 0 oder 1 gesetzt wird und kein anderer Wert, die zweite Anweisung nicht.

John Scipione
quelle
2
Das ist völlig falsch. Die zweite Aussage beinhaltet eine implizite int-> boolBesetzung. Dies ist genau definiert, und alle Nicht-Null- intWerte werden in trueund 0 in konvertiert false. Die Warnung, die VC ++ dort ausgibt, ist eigentlich eine "Leistungswarnung" und hat damit nichts zu tun.
Pavel Minaev
Ich denke, Sie verwechseln BOOL mit Bool. Mit bool erhalten Sie einen echten Bool mit einer impliziten Besetzung, also wahr oder falsch.
Recep
2
Die Zustände eines Bools in C ++ sind nicht 1 und 0, sondern wahr und falsch.
Johannes Schaub - litb
Danke, das wusste ich nicht. Ich dachte, dass 0 == falsch und 1 == wahr, aber anscheinend ist das nicht so.
John Scipione
Sie hatten das erste Mal Recht John Scipione, dies hat seinen Platz
Matt Joiner
4

!!ist eine idiomatische Methode zum Konvertieren boolund funktioniert, um die dumme Warnung des Visual C ++ - Compilers über die angebliche Ineffizienz einer solchen Konvertierung zu beenden.

Ich sehe an den anderen Antworten und Kommentaren, dass viele Leute mit der Nützlichkeit dieser Redewendung in der Windows-Programmierung nicht vertraut sind. Das heißt, sie haben keine ernsthafte Windows-Programmierung durchgeführt. Und nehmen Sie blind an, dass das, was ihnen begegnet ist, repräsentativ ist (es ist nicht).

#include <iostream>
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): Warnung C4800: 'int': Erzwingt den Wert bool 'true' oder 'false' (Leistungswarnung)

[d: \ dev \ test]
> _

Und mindestens eine Person denkt, wenn ein Anfänger seine Bedeutung nicht erkennt, ist es nicht gut. Nun, das ist dumm. Es gibt eine Menge, die Anfänger nicht erkennen oder verstehen. Den eigenen Code so zu schreiben, dass er von jedem Anfänger verstanden wird, ist nichts für Profis. Nicht einmal für Studenten. Beginnen wir auf dem Weg, Operatoren und Operatorkombinationen auszuschließen, die Anfänger nicht erkennen ... Nun, ich habe nicht die richtigen Worte, um diesem Ansatz eine angemessene Beschreibung zu geben, sorry.

Prost und hth. - Alf
quelle
1

Die Antwort von user143506 ist richtig, aber für ein mögliches Leistungsproblem habe ich die Möglichkeiten in asm verglichen:

return x;, return x != 0;, return !!x;Und auch return boolean_cast<bool>(x)Ergebnisse in diesem perfekten Satz von asm Anweisungen:

test    edi/ecx, edi/ecx
setne   al
ret

Dies wurde für GCC 7.1 und MSVC 19 2017 getestet. (Nur der boolean_converter in MSVC 19 2017 führt zu einer größeren Menge an asm-Code, dies wird jedoch durch Vorlagen und Strukturen verursacht und kann aus Sicht der Leistung vernachlässigt werden, da dies auch der Fall ist Zeilen wie oben angegeben können nur für verschiedene Funktionen mit derselben Laufzeit dupliziert werden.)

Dies bedeutet: Es gibt keinen Leistungsunterschied.

PS: Dieser boolean_cast wurde verwendet:

#define BOOL int
// primary template
template< class TargetT, class SourceT >
struct boolean_converter;

// full specialization
template< >
struct boolean_converter<bool, BOOL>
{
  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<TargetT, SourceT> converter_t;
  return converter_t::convert(b);
}

bool is_non_zero(int x) {
   return boolean_cast< bool >(x);
}
Kallitokaco
quelle
-1

Kein großer Grund, außer paranoid zu sein oder durch Code zu schreien, dass es ein Idiot ist.

Für den Compiler macht es am Ende keinen Unterschied.

Lerner
quelle
Der generierte Code kann durchaus "langsamer" sein: Die Ganzzahl erfordert einen Test, um festzustellen, ob sie Null ist. Für einen Booleschen Wert, der als 0/1-Binärdatei dargestellt wird, ist jedoch kein Test erforderlich.
Johannes Schaub - Litb
2
Aber nun, mit diesem Rational sollten wir eine Leistungswarnung für jeden virtuellen Funktionsaufruf haben ...
gimpf
-2

Ich habe diese Technik der Konvertierung in einen boolDatentyp noch nie gemocht - sie riecht falsch!

Stattdessen verwenden wir ein handliches Template namens boolean_castgefunden hier . Es ist eine flexible Lösung, die expliziter ist und wie folgt verwendet werden kann:

bool IsWindow = boolean_cast< bool >(::IsWindow(hWnd));
Alan
quelle
8
Es ist ein bisschen übertrieben, nicht wahr? Ich meine, am Ende des Tages ist die Umstellung auf Bool ein Präzisionsverlust und läuft auf so etwas wie Return b hinaus. wahr falsch; Das ist ungefähr das Gleiche wie !! TRUE Persönlich ist dies ein überentwickelter STL-ähnlicher Fetisch.
Igor Zevaka
Ich gehe davon aus, dass Sie mit der Verwendung von boolean_cast nicht einverstanden sind (daher die Abwärtsabstimmung) - was würden Sie vorschlagen, dass wir dann verwenden?
Alan
5
Ich schlage vor, Sie verwenden die Sprache so, wie sie verwendet werden sollte. Hohe Leistung und böse. Schneiden Sie einfach die BS und erledigen Sie die Arbeit.
Matt Joiner