I just came onto a project with a pretty huge code base.
I\'m mostly dealing with C++ and a lot of the code they write uses double negation for their boolean logic.
!!
was used to cope with original C++ which did not have a boolean type (as neither did C).
Example Problem:
Inside if(condition)
, the condition
needs to evaluate to some type like double, int, void*
, etc., but not bool
as it does not exist yet.
Say a class existed int256
(a 256 bit integer) and all integer conversions/casts were overloaded.
int256 x = foo();
if (x) ...
To test if x
was "true" or non-zero, if (x)
would convert x
to some integer and then assess if that int
was non-zero. A typical overload of (int) x
would return only the LSbits of x
. if (x)
was then only testing the LSbits of x
.
But C++ has the !
operator. An overloaded !x
would typically evaluate all the bits of x
. So to get back to the non-inverted logic if (!!x)
is used.
Ref Did older versions of C++ use the `int` operator of a class when evaluating the condition in an `if()` statement?
It's a trick to convert to bool.
Is operator! overloaded?
If not, they're probably doing this to convert the variable to a bool without producing a warning. This is definitely not a standard way of doing things.
If variable is of object type, it might have a ! operator defined but no cast to bool (or worse an implicit cast to int with different semantics. Calling the ! operator twice results in a convert to bool that works even in strange cases.
It's actually a very useful idiom in some contexts. Take these macros (example from the Linux kernel). For GCC, they're implemented as follows:
#define likely(cond) (__builtin_expect(!!(cond), 1))
#define unlikely(cond) (__builtin_expect(!!(cond), 0))
Why do they have to do this? GCC's __builtin_expect
treats its parameters as long
and not bool
, so there needs to be some form of conversion. Since they don't know what cond
is when they're writing those macros, it is most general to simply use the !!
idiom.
They could probably do the same thing by comparing against 0, but in my opinion, it's actually more straightforward to do the double-negation, since that's the closest to a cast-to-bool that C has.
This code can be used in C++ as well... it's a lowest-common-denominator thing. If possible, do what works in both C and C++.
The coders think that it will convert the operand to bool, but because the operands of && are already implicitly converted to bool, it's utterly redundant.