Double Negation in C++
Asked Answered
R

14

147

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.

 if (!!variable && (!!api.lookup("some-string"))) {
       do_some_stuff();
 }                                   

I know these guys are intelligent programmers, it's obvious they aren't doing this by accident.

I'm no seasoned C++ expert, my only guess at why they are doing this is that they want to make absolutely positive that the value being evaluated is the actual boolean representation. So they negate it, then negate that again to get it back to its actual boolean value.

Is this correct, or am I missing something?

Recessional answered 29/10, 2008 at 22:45 Comment(3)
check here, already asked, Is !! a safe way to convert to bool in C++?Contagium
This topic has been discussed here.Trinitrocresol
Possible duplicate of Is !! a safe way to convert to bool in C++?Contrapose
G
140

It's a trick to convert to bool.

Geese answered 29/10, 2008 at 22:47 Comment(7)
Exactly right, it's a common idiom in most languages where ! is the negation operator with an implicit cast to booleanHafnium
I think cast it explicitly with (bool) would be clearer, why use this tricky !!, because it is of less typing?Crick
However, it's pointless in C++ or modern C, or where the result is only used in a boolean expression (as in the question). It was useful back when we had no bool type, to help avoid storing values other than 1 and 0 in boolean variables.Lvov
I agree with @baiyanhuang... surely it only decreases readability at no real gain?Joyann
@lzprgmr: explicit cast causes a "performance warning" on MSVC. Using !! or !=0 solves the problem, and among the two I find the former cleaner (since it will work on a greater amount of types). Also I agree that there is no reason to use either in the code in question.Linotype
@Noldorin, I think it improves readability - if you know what it means, it is simple, neat and logical.Bagatelle
Improves? Bloody hell... give me some of whatever you're smoking.Joyann
H
78

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++.

Hourglass answered 30/10, 2008 at 4:50 Comment(2)
I think this makes a lot of sense when you think through it. I haven't read every answer, but it seems like the conversion process isn't specified. If we have a value with 2 bits high and'd with a value that has only one bit high, we will have a non-zero value. Negating a non-zero value results in a boolean conversion (false if it's zero, true otherwise). Then negating again results in a boolean that represents the original truth.Blowbyblow
Since SO won't allow me to update my comment, I will add a fix to my mistake. Negating an integral value results in a boolean conversion (false if it's non-zero, true otherwise).Blowbyblow
A
53

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.

Autogiro answered 29/10, 2008 at 23:0 Comment(3)
Visual C++ gives a performance wanring in some cases without this trick.Sandstorm
I'd suppose it'd be best to just disable the warning than to work around useless warnings in the code.Alveolate
Perhaps they don't realize it. This does make perfect sense in the context of a macro though, where you could be working with integral data types that you're not aware of. Consider objects with parenthesis operator overloaded to return integral value representing a bit field.Blowbyblow
T
14

Yes it is correct and no you are not missing something. !! is a conversion to bool. See this question for more discussion.

Trinitarianism answered 29/10, 2008 at 22:49 Comment(0)
C
13

It's a technique to avoid writing (variable != 0) - i.e. to convert from whatever type it is to a bool.

IMO Code like this has no place in systems that need to be maintained - because it is not immediately readable code (hence the question in the first place).

Code must be legible - otherwise you leave a time debt legacy for the future - as it takes time to understand something that is needlessly convoluted.

Cavalcade answered 29/10, 2008 at 22:56 Comment(2)
My definition of a trick is something that not everyone can understand at the first reading. Something that needs figuring out is a trick. Also horrible because the ! operator could be overloaded...Cavalcade
@orlandu63: simple typecasting is bool(expr): it does the right thing and everybody understand the intent at first sight. !!(expr) is a double negation, which accidentally converts to bool... this is not simple.Thiele
L
9

It side-steps a compiler warning. Try this:

int _tmain(int argc, _TCHAR* argv[])
{
    int foo = 5;
    bool bar = foo;
    bool baz = !!foo;
    return 0;
}

The 'bar' line generates a "forcing value to bool 'true' or 'false' (performance warning)" on MSVC++, but the 'baz' line sneaks through fine.

Lei answered 31/10, 2008 at 10:56 Comment(1)
Most commonly encountered in the Windows API itself which doesn't know about the bool type - everything is encoded as 0 or 1 in an int.Kandicekandinsky
F
6

Legacy C developers had no Boolean type, so they often #define TRUE 1 and #define FALSE 0 and then used arbitrary numeric data types for Boolean comparisons. Now that we have bool, many compilers will emit warnings when certain types of assignments and comparisons are made using a mixture of numeric types and Boolean types. These two usages will eventually collide when working with legacy code.

To work around this problem, some developers use the following Boolean identity: !num_value returns bool true if num_value == 0; false otherwise. !!num_value returns bool false if num_value == 0; true otherwise. The single negation is sufficient to convert num_value to bool; however, the double negation is necessary to restore the original sense of the Boolean expression.

This pattern is known as an idiom, i.e., something commonly used by people familiar with the language. Therefore, I don't see it as an anti-pattern, as much as I would static_cast<bool>(num_value). The cast might very well give the correct results, but some compilers then emit a performance warning, so you still have to address that.

The other way to address this is to say, (num_value != FALSE). I'm okay with that too, but all in all, !!num_value is far less verbose, may be clearer, and is not confusing the second time you see it.

Flavoprotein answered 3/12, 2014 at 0:13 Comment(0)
T
4

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.

Toweling answered 29/10, 2008 at 22:48 Comment(0)
K
2

!! 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?

Kocher answered 25/8, 2014 at 23:14 Comment(0)
C
1

As Marcin mentioned, it might well matter if operator overloading is in play. Otherwise, in C/C++ it doesn't matter except if you're doing one of the following things:

  • direct comparison to true (or in C something like a TRUE macro), which is almost always a bad idea. For example:

    if (api.lookup("some-string") == true) {...}

  • you simply want something converted to a strict 0/1 value. In C++ an assignment to a bool will do this implicitly (for those things that are implicitly convertible to bool). In C or if you're dealing with a non-bool variable, this is an idiom that I've seen, but I prefer the (some_variable != 0) variety myself.

I think in the context of a larger boolean expression it simply clutters things up.

Cristen answered 29/10, 2008 at 23:8 Comment(0)
Y
1

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.

Yawl answered 11/5, 2012 at 19:24 Comment(0)
S
1

This may be an example of the double-bang trick (see The Safe Bool Idiom for more details). Here I summarize the first page of the article.

In C++ there are a number of ways to provide Boolean tests for classes.

An obvious way is the operator bool conversion operator.

// operator bool version
class Testable {
    bool ok_;
    public:
    explicit Testable(bool b = true) : ok_(b) {}

    operator bool() const { // use bool conversion operator
      return ok_;
    }
};

We can test the class as thus:

Testable test;
if (test) {
    std::cout << "Yes, test is working!\n";
}
else { 
    std::cout << "No, test is not working!\n";
}

However, operator bool is considered unsafe because it allows nonsensical operations such as test << 1; or int i = test.

Using operator! is safer because we avoid implicit conversion or overloading issues.

The implementation is trivial,

bool operator!() const { // use operator!
    return !ok_;
}

The two idiomatic ways to test Testable object are

Testable test;
if (!!test) {
    std::cout << "Yes, test is working!\n";
}
if (!test) {
    std::cout << "No, test is not working!\n";
}

The first version if (!!test) is what some people call the double-bang trick.

Splatter answered 12/10, 2016 at 14:53 Comment(1)
Since C++11, one can use explicit operator bool to prevent implicit conversion to other integral types.Scotia
D
0

It's correct but, in C, pointless here -- 'if' and '&&' would treat the expression the same way without the '!!'.

The reason to do this in C++, I suppose, is that '&&' could be overloaded. But then, so could '!', so it doesn't really guarantee you get a bool, without looking at the code for the types of variable and api.call. Maybe someone with more C++ experience could explain; perhaps it's meant as a defense-in-depth sort of measure, not a guarantee.

Distort answered 29/10, 2008 at 23:2 Comment(1)
The compiler would treat the values the same either way if it's only used as an operand to an if or &&, but using !! may help on some compilers if if (!!(number & mask)) gets replaced with bit triggered = !!(number & mask); if (triggered); on some embedded compilers with bit types, assigning e.g. 256 to a bit type will yield zero. Without the !!, the apparently-safe transformation (copying the if condition to a variable and then branching) won't be safe.Streetlight
M
0

Maybe the programmers were thinking something like this...

!!myAnswer is boolean. In context, it should become boolean, but I just love to bang bang things to make sure, because once upon a time there was a mysterious bug that bit me, and bang bang, I killed it.

Masse answered 29/10, 2008 at 23:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.