[英]Why use !! when converting int to bool?
以這種方式將整數轉換為布爾值的原因是什么?
bool booleanValue = !!integerValue;
而不僅僅是
bool booleanValue = integerValue;
我所知道的是,在 VC++7 中,后者會導致C4800 警告,而前者不會。 兩者之間還有其他區別嗎?
“!!”的問題習語是它很簡潔,很難看,很容易被誤認為是打字錯誤,很容易去掉一個“!”,等等。 我把它歸入“看看我們可以用 C/C++ 多可愛”的類別。
只需寫bool isNonZero = (integerValue != 0);
... 清楚。
從歷史上看, !!
idiom 用於確保您的 bool 確實包含bool
類變量中預期的兩個值之一,因為 C 和 C++ 沒有真正的bool
類型,我們用int
偽造了它。 現在對於“真正的” bool
s,這不是一個問題。
但是使用!!
是一種有效的記錄方式(對於編譯器和任何未來在您的代碼中工作的人),是的,您確實打算將該int
轉換為bool
。
使用它是因為 C 語言(以及一些預標准的 C++ 編譯器)沒有bool
類型,只有int
。 所以int
被用來表示邏輯值: 0
應該表示false
,其他一切都是true
。 !
運算符返回1
,從0
和0
的一切。 雙!
被用來反轉這些,它在那里確保值只是0
或1
具體取決於其邏輯值。
在 C++ 中,由於引入了適當的bool
類型,因此不再需要這樣做。 但是,由於 C 與 C++ 的向后兼容性(大多數情況下),您不能只更新所有遺留源,而且您也不應該這樣做。 但是很多人仍然這樣做,出於同樣的原因:保持他們的代碼向后兼容仍然不理解bool
的舊編譯器。
這是唯一真正的答案。 其他答案具有誤導性。
因為 !integerValue 意味着 integerValue == 0 並且 !!integerValue 因此意味着 integerValue != 0,一個返回 bool 的有效表達式。 后者是信息丟失的演員表。
另一種選擇是三元運算符,它似乎生成的匯編代碼少一行(無論如何在 Visual Studio 2005 中):
bool ternary_test = ( int_val == 0 ) ? false : true;
產生匯編代碼:
cmp DWORD PTR _int_val$[ebp], 0
setne al
mov BYTE PTR _ternary_test$[ebp], al
相對:
bool not_equal_test = ( int_val != 0 );
它產生:
xor eax, eax
cmp DWORD PTR _int_val$[ebp], 0
setne al
mov BYTE PTR _not_equal_test$[ebp], al
我知道這不是一個巨大的差異,但我對此很好奇,只是想我會分享我的發現。
bool 只能有 0 和 1 兩種狀態。假設有符號的 32 位整數,整數可以具有從 -2147483648 到 2147483647 的任何狀態。 一元! 如果輸入為 0,則運算符輸出 1,如果輸入為 0 以外的任何內容,則輸出 0。因此 !0 = 1 且 !234 = 0。第二個 ! 簡單地切換輸出,使 0 變為 1,1 變為 0。
因此,第一條語句保證 booleanValue 將被設置為等於 0 或 1 且沒有其他值,第二條語句則不然。
!!
是轉換為bool
的慣用方式,它可以關閉 Visual C++ 編譯器關於此類轉換效率低下的愚蠢警告。
我從其他答案和評論中看到,很多人不熟悉這個習語在 Windows 編程中的用處。 這意味着他們沒有做過任何嚴肅的 Windows 編程。 並盲目地假設他們遇到的事情具有代表性(事實並非如此)。
#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] > _
至少有一個人認為,如果一個徹頭徹尾的新手不認識它的含義,那就不好了。 嗯,這很愚蠢。 有很多完全是新手不認識或不理解的。 編寫一個代碼以便任何新手都能理解它並不是專業人士的事情。 甚至不適合學生。 從排除完全新手不認識的運算符和運算符組合的路徑開始......好吧,我沒有詞來給這種方法一個適當的描述,抱歉。
user143506 的答案是正確的,但對於可能的性能問題,我比較了 asm 中的可能性:
return x;
, return x != 0;
, return !!x;
甚至return boolean_cast<bool>(x)
結果在這組完美的 asm 指令中:
test edi/ecx, edi/ecx
setne al
ret
這已針對 GCC 7.1 和 MSVC 19 2017 進行了測試。(只有 MSVC 19 2017 中的 boolean_converter 導致更多的 asm 代碼,但這是由模板化和結構引起的,從性能的角度來看可以忽略不計,因為相同上面提到的行可能只是為具有相同運行時的不同函數重復。)
這意味着:沒有性能差異。
PS:使用了這個 boolean_cast:
#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);
}
除了偏執或通過代碼大喊它是一個布爾之外,沒有什么大的理由。
對於編譯器最終它不會有所作為。
我從不喜歡這種轉換為bool
數據類型的技術——它聞起來不對!
相反,我們使用了一個名為boolean_cast
的便捷模板, boolean_cast
在此處找到。 這是一個靈活的解決方案,它在做什么方面更加明確,可以按如下方式使用:
bool IsWindow = boolean_cast< bool >(::IsWindow(hWnd));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.