在将 int 转换为 bool 时,为什么要使用! ! ?

用这种方法将整数转换为布尔值的原因是什么?

bool booleanValue = !!integerValue;

而不是仅仅

bool booleanValue = integerValue;

我所知道的是,在 VC + + 7中,后者会导致 C4800警告,而前者不会。这两者之间还有什么区别吗?

50603 次浏览

Because !integerValue means integerValue == 0 and !!integerValue thus means integerValue != 0, a valid expression returning a bool. The latter is a cast with information loss.

Historically, the !! idiom was used to ensure that your bool really contained one of the two values expected in a bool-like variable, because C and C++ didn't have a true bool type and we faked it with ints. This is less of an issue now with "real" bools.

But using !! is an efficient means of documenting (for both the compiler and any future people working in your code) that yes, you really did intend to cast that int to a bool.

A bool can only have two states, 0, and 1. An integer can have any state from -2147483648 to 2147483647 assuming a signed 32-bit integer. The unary ! operator outputs 1 if the input is 0 and outputs 0 if the input is anything except 0. So !0 = 1 and !234 = 0. The second ! simply switches the output so 0 becomes 1 and 1 becomes 0.

So the first statement guarantees that booleanValue will be be set equal to either 0 or 1 and no other value, the second statement does not.

The problems with the "!!" idiom are that it's terse, hard to see, easy to mistake for a typo, easy to drop one of the "!'s", and so forth. I put it in the "look how cute we can be with C/C++" category.

Just write bool isNonZero = (integerValue != 0); ... be clear.

No big reason except being paranoid or yelling through code that its a bool.

for compiler in the end it wont make difference .

I've never like this technique of converting to a bool data type - it smells wrong!

Instead, we're using a handy template called boolean_cast found here. It's a flexible solution that's more explicit in what it's doing and can used as follows:

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

!! is an idiomatic way to convert to bool, and it works to shut up the Visual C++ compiler's sillywarning about alleged inefficiency of such conversion.

I see by the other answers and comments that many people are not familiar with this idiom's usefulness in Windows programming. Which means they haven't done any serious Windows programming. And assume blindly that what they have encountered is representative (it is not).

#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) : warning C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning)


[d:\dev\test]
> _

And at least one person thinks that if an utter novice does not recognize its meaning, then it's ungood. Well that's stupid. There's a lot that utter novices don't recognize or understand. Writing one's code so that it will be understood by any utter novice is not something for professionals. Not even for students. Starting on the path of excluding operators and operator combinations that utter novices don't recognize... Well I don't have the words to give that approach an appropriate description, sorry.

It is used because the C language (and some pre-standard C++ compilers too) didn't have the bool type, just int. So the ints were used to represent logical values: 0 was supposed to mean false, and everything else was true. The ! operator was returning 1 from 0 and 0 from everything else. Double ! was used to invert those, and it was there to make sure that the value is just 0 or 1 depending on its logical value.

In C++, since introducing a proper bool type, there's no need to do that anymore. But you cannot just update all legacy sources, and you shouldn't have to, due to backward compatibility of C with C++ (most of the time). But many people still do it, from the same reason: to remain their code backward-compatible with old compilers which still don't understand bools.

And this is the only real answer. Other answers are misleading.

Another option is the ternary operator which appears to generate one line less of assembly code (in Visual Studio 2005 anyways):

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

which produces the assembly code:

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

Versus:

bool not_equal_test = ( int_val != 0 );

which produces:

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

I know it isn't a huge difference but I was curious about it and just thought that I would share my findings.

The answer of user143506 is correct but for a possible performance issue I compared the possibilies in asm:

return x;, return x != 0;, return !!x; and even return boolean_cast<bool>(x) results in this perfect set of asm instructions:

test    edi/ecx, edi/ecx
setne   al
ret

This was tested for GCC 7.1 and MSVC 19 2017. (Only the boolean_converter in MSVC 19 2017 results in a bigger amount of asm-code but this is caused by templatization and structures and can be neglected by a performance point of view, because the same lines as noted above may just duplicated for different functions with the same runtime.)

This means: There is no performance difference.

PS: This boolean_cast was used:

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