2008-10-29 87 views
100

我剛剛進入一個具有相當龐大代碼庫的項目。C++代碼中的雙重否定

我主要處理C++和他們編寫的很多代碼使用雙重否定他們的布爾邏輯。

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

我知道這些傢伙都是聰明的程序員,很明顯他們不是偶然這樣做的。

我不是經驗豐富的C++專家,我唯一的猜測是他們爲什麼要這樣做,是因爲他們想要絕對肯定被評估的值是實際的布爾表示。所以他們否定它,然後再次否定它以使其回到其實際的布爾值。

這是正確的,還是我錯過了什麼?

+0

此主題已經被討論[這裏](http://stackoverflow.com/questions/206106/is-a-safe-way-to-convert-to-bool-in-c#206122)。 – Dima 2008-10-30 14:01:33

+4

在這裏檢查,已經問過,[是!一個安全的方式來轉換爲C++布爾?](http://stackoverflow.com/questions/206106/is-a-safe-way-to-convert-to-bool-in-c) – 2008-10-29 22:52:17

+0

可能重複[Is !一個安全的方式來轉換爲C++布爾?](https://stackoverflow.com/questions/206106/is-a-safe-way-to-convert-to-bool-in-c) – EdChum 2017-12-04 16:46:34

回答

104

這是一個轉換爲布爾的技巧。

+3

是的,它是一個大多數語言的常用成語在哪!是一個隱式轉換爲布爾型的否定運算符 – Gareth 2008-10-29 22:49:16

+13

我認爲用(bool)明確地轉換它會更清楚,爲什麼要使用這個棘手的!!,因爲它的輸入較少? – 2010-03-29 03:39:24

+21

但是,在C++或現代C語言中,或僅在布爾表達式中使用結果(如問題中),這是毫無意義的。當我們沒有`bool`類型時,這有助於避免在布爾變量中存儲'1`和`0'以外的值。 – 2012-01-17 19:01:05

5

是運營商!超載?
如果沒有,他們可能會這樣做,將變量轉換爲布爾值而不會產生警告。這絕對不是一種標準的做事方式。

10

是的,這是正確的,沒有你不會錯過什麼。 !!是對bool的轉換。有關更多討論,請參閱this question

9

這是一種避免寫入的技術(變量!= 0) - 即從任何類型轉換爲布爾值。

像這樣的IMO代碼在需要維護的系統中沒有位置 - 因爲它不是立即可讀的代碼(因此首先是問題)。

代碼必須清晰可讀 - 否則您將爲未來留下時間債務遺留問題 - 因爲需要時間來理解無用的錯誤。

47

編碼器認爲它會將操作數轉換爲布爾值,但由於& &的操作數已經隱式轉換爲布爾值,所以它是完全多餘的。

0

這是正確的,但在C中,這裏沒有意義 - 'if'和'& &'將以相同的方式對待表達式,而不使用'!!'。

我想在C++中這樣做的原因是'& &'可能被重載。但是,那麼'!',所以它不真的保證你得到一個布爾,而不看代碼的類型variableapi.call。也許有更多C++經驗的人可以解釋;也許這意味着作爲一種縱深防禦措施,而不是保證。

1

由於提到Marcin,如果運算符超載正在發揮可能很重要。否則,在C/C++不會,除非你正在做下面的事情一件事:

  • 直接比較true(或C中像一個TRUE宏),這是幾乎總是一個餿主意。例如:

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

  • 你只是想要的東西轉換成嚴格的0/1值。在C++中,對bool的分配將隱含地執行此操作(對於那些可以隱式轉換爲bool的操作)。在C語言中,如果你正在處理一個非bool變量,這是我見過的一個成語,但我更喜歡(some_variable != 0)這個變種。

我認爲在一個更大的布爾表達式的上下文中,它只是混亂了事情。

0

也許程序員在想這樣的事情......

!! myAnswer是布爾值。在上下文中,它應該變成布爾型,但我只是喜歡砰的一聲,以確保,因爲曾經有一個神祕的蟲子咬我,砰的一聲,我殺了它。

64

在某些情況下,它實際上是一個非常有用的習慣用法。以這些宏爲例(來自Linux內核的示例)。對於海灣合作委員會,他們實施如下:

#define likely(cond) (__builtin_expect(!!(cond), 1)) 
#define unlikely(cond) (__builtin_expect(!!(cond), 0)) 

他們爲什麼要這樣做? GCC的__builtin_expect將其參數視爲long而不是bool,因此需要進行某種形式的轉換。由於他們不知道cond是什麼時候編寫這些宏,所以最簡單地使用!!成語。

他們或許可以通過與0進行比較來做同樣的事情,但在我看來,做雙重否定實際上更直截了當,因爲這是C最接近於cast-to-bool的。

這段代碼也可以在C++中使用......它是一個最小公分母。如果可能的話,在C和C++中執行哪些操作。

8

它側編譯警告。試試這個:

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

的「酒吧」行生成一個「強制值bool‘真’或‘假’(性能警告)」關於MSVC++,但「巴茲」線經過精細告密。

1

如果變量是對象類型,它可能有一個!運算符定義但不投射到bool(或者更糟糕的是隱式投射到具有不同語義的int)兩次調用!運算符會導致轉換爲bool,即使在奇怪的情況下也可以運行

1

!!被用來處理原始C++沒有一個布爾類型(也沒有C)


例問題:

內部if(condition),所述condition需求評估到某種類型的像double, int, void*,等,但不bool因爲它不存在。

說一個類存在int256(一個256位整數),所有整數轉換/轉換過載。

int256 x = foo(); 
if (x) ... 

爲了測試是否x是「真」或非零,if (x)將轉換x一些整數,然後評估如果int是非零。 (int) x的典型超載將只返回x的LSbits。 if (x)然後僅測試x的LS位。

但是C++有!運算符。過載的!x通常會評估x的所有位。因此使用返回非反轉邏輯if (!!x)

Did older versions of C++ use the `int` operator of a class when evaluating the condition in an `if()` statement?

2

傳統C開發者沒有布爾類型,因此他們經常#define TRUE 1#define FALSE 0然後使用任意的數值數據類型爲布爾比較。現在我們有了bool,當使用數字類型和布爾類型的混合進行某些類型的賦值和比較時,許多編譯器會發出警告。這兩種用法在使用遺留代碼時最終會發生衝突。

要解決此問題,一些開發人員使用以下布爾標識:!num_value返回bool true如果num_value == 0;否則爲false!!num_value如果num_value == 0返回bool false;否則爲true。單個否定就足以將num_value轉換爲bool;然而,雙重否定對於恢復布爾表達式的原始意義是必要的。

這種模式被稱爲成語,即熟悉該語言的人們通常使用的東西。因此,我不認爲它是一種反模式,就像我會static_cast<bool>(num_value)一樣。演員陣容可能會給出正確的結果,但一些編譯器會發出性能警告,因此您仍然必須解決這個問題。

解決這個問題的另一種方式是,(num_value != FALSE)。我也可以,但總而言之,!!num_value遠不那麼冗長,可能會更清晰,並且在你第二次看到它時不會感到困惑。

0

這可能是雙重伎倆的示例,請參閱The Safe Bool Idiom瞭解更多詳情。這裏我總結了文章的第一頁。

在C++中有很多方法可以爲類提供布爾測試。

一個明顯的方法是operator bool轉換運算符。

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

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

我們可以測試類,

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

然而,opereator bool被認爲是不安全的,因爲它允許無意義的操作,如test << 1;int i=test

使用operator!更安全是因爲我們避免了隱式轉換或重載問題。

實現很簡單,

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

兩個慣用的方法來測試Testable對象是

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

第一個版本if (!!test)就是一些人所說的雙響招