136

我刚刚进入一个拥有非常庞大的代码库的项目。

我主要处理 C++ 并且他们编写的许多代码都使用双重否定作为布尔逻辑。

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

我知道这些人是聪明的程序员,很明显他们这样做不是偶然的。

我不是经验丰富的 C++ 专家,我对他们为什么这样做的唯一猜测是他们想要绝对肯定被评估的值是实际的布尔表示。所以他们否定它,然后再次否定它以使其回到它的实际布尔值。

这是正确的,还是我错过了什么?

4

14 回答 14

132

转换为布尔值是一个技巧。

于 2008-10-29T22:47:07.023 回答
75

在某些情况下,它实际上是一个非常有用的习语。使用这些宏(来自 Linux 内核的示例)。对于 GCC,它们的实现方式如下:

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

为什么他们必须这样做?GCC__builtin_expect将其参数视为long和 not bool,因此需要进行某种形式的转换。由于他们不知道cond在编写这些宏时是什么,因此最普遍的做法是简单地使用!!成语。

他们可能可以通过与 0 进行比较来做同样的事情,但在我看来,进行双重否定实际上更直接,因为这是最接近 C 的转换为布尔值的方法。

这段代码也可以在 C++ 中使用……这是最低公分母的事情。如果可能的话,做在 C 和 C++ 中都有效的事情。

于 2008-10-30T04:50:27.413 回答
53

编码人员认为它将操作数转换为布尔值,但因为 && 的操作数已经隐式转换为布尔值,所以完全是多余的。

于 2008-10-29T23:00:47.337 回答
14

是的,它是正确的,不,你没有错过什么。 !!是转换为布尔值。有关更多讨论,请参阅此问题

于 2008-10-29T22:49:55.130 回答
12

这是一种避免写入(变量!= 0)的技术 - 即从任何类型转换为布尔值。

像这样的 IMO 代码在需要维护的系统中没有位置 - 因为它不是立即可读的代码(因此首先是问题)。

代码必须清晰易读 - 否则你会为未来留下时间债务遗产 - 因为理解一些不必要的复杂事物需要时间。

于 2008-10-29T22:56:27.830 回答
9

它避开了编译器警告。试试这个:

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

'bar' 行在 MSVC++ 上生成一个“强制值 bool 'true' 或 'false'(性能警告)”,但 'baz' 行可以很好地通过。

于 2008-10-31T10:56:39.420 回答
6

传统 C 开发人员没有布尔类型,因此他们经常#define TRUE 1使用#define FALSE 0任意数字数据类型进行布尔比较。现在我们有了bool,当使用数字类型和布尔类型的混合进行某些类型的赋值和比较时,许多编译器会发出警告。在处理遗留代码时,这两种用法最终会发生冲突。

为了解决这个问题,一些开发人员使用以下布尔标识:!num_value返回bool trueif num_value == 0; false除此以外。如果!!num_value返回;除此以外。单个否定足以转换为; 然而,双重否定是恢复布尔表达式的原始意义所必需的。bool falsenum_value == 0truenum_valuebool

这种模式被称为成语,即熟悉该语言的人常用的东西。因此,我不认为它是一种反模式,就像我一样static_cast<bool>(num_value)。强制转换很可能会给出正确的结果,但是一些编译器会发出性能警告,所以你仍然必须解决这个问题。

解决这个问题的另一种方法是,(num_value != FALSE). 我也同意,但总而言之,!!num_value它不那么冗长,可能更清晰,并且不会在您第二次看到它时感到困惑。

于 2014-12-03T00:13:20.457 回答
4

是运营商!超载?
如果不是,他们可能这样做是为了将变量转换为布尔值而不产生警告。这绝对不是标准的做事方式。

于 2008-10-29T22:48:48.440 回答
2

!!用于处理没有布尔类型的原始 C++(C 也没有)。


示例问题:

在内部if(condition)condition需要评估某种类型,例如double, int, void*等,但不存在,bool因为它还不存在。

假设存在一个类int256(一个 256 位整数)并且所有整数转换/转换都被重载。

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

要测试x是“真”还是非零,if (x)将转换x为某个整数,然后评估它是否int非零。的典型重载(int) x将仅返回 的 LSbits xif (x)当时只测试x.

但是 C++ 有!运算符。重载!x通常会评估x. 所以要回到非反相逻辑if (!!x)

参考旧版本的 C++ 在评估“if()”语句中的条件时是否使用了类的“int”运算符?

于 2014-08-25T23:14:12.823 回答
1

正如Marcin所提到的,运算符重载是否在起作用可能很重要。否则,在 C/C++ 中,这无关紧要,除非您正在执行以下操作之一:

  • 直接比较true(或在 C 中类似于TRUE宏),这几乎总是一个坏主意。例如:

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

  • 您只是希望将某些东西转换为严格的 0/1 值。在 C++ 中,对 a 的赋值bool会隐式执行此操作(对于那些可以隐式转换为 的东西bool)。在 C 语言中,或者如果您正在处理非布尔变量,这是我见过的一个习语,但我(some_variable != 0)自己更喜欢这种变化。

我认为在更大的布尔表达式的上下文中,它只会使事情变得混乱。

于 2008-10-29T23:08:24.077 回答
1

如果变量是对象类型,它可能有一个!运算符已定义,但没有强制转换为 bool(或更糟糕的是,隐式强制转换为具有不同语义的 int。两次调用 ! 运算符会导致转换为 bool,即使在奇怪的情况下也能正常工作。

于 2012-05-11T19:24:09.393 回答
1

这可能是双爆技巧的一个例子(有关更多详细信息,请参阅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";
}

但是,operator bool它被认为是不安全的,因为它允许无意义的操作,例如test << 1;or int i = test

使用operator!更安全,因为我们避免了隐式转换或重载问题。

实现很简单,

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

Testable测试对象的两种惯用方法是

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

第一个版本if (!!test)就是一些人所说的双爆技巧

于 2016-10-12T14:53:46.583 回答
0

这是正确的,但在 C 语言中,这里没有意义——'if' 和 '&&' 在没有 '!!' 的情况下会以同样的方式处理表达式。

我想,在 C++ 中这样做的原因是 '&&' 可能会被重载。但是,'!' 也可以,所以它并不能真正保证你得到一个布尔值,而无需查看variableand类型的代码api.call。也许有更多 C++ 经验的人可以解释一下;也许这意味着作为一种纵深防御措施,而不是保证。

于 2008-10-29T23:02:55.133 回答
0

也许程序员正在考虑这样的事情......

!!myAnswer 是布尔值。在上下文中,它应该变成布尔值,但我只是喜欢用砰砰来确定,因为从前有一个神秘的虫子咬了我,砰砰,我杀了它。

于 2008-10-29T23:31:08.960 回答