C++代码中的双重否定

Bri*_*aro 116 c++ boolean

我刚刚进入一个拥有相当庞大代码库的项目.

我主要处理C++,他们编写的很多代码都使用布尔逻辑的双重否定.

 if (!!variable && (!!api.lookup("some-string"))) {
       do_some_stuff();
 }                                   
Run Code Online (Sandbox Code Playgroud)

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

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

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

Don*_*eld 114

这是转换为布尔的技巧.

  • 但是,它在C++或现代C中没有意义,或者结果仅用于布尔表达式(如问题中所示).当我们没有`bool`类型时,它很有用,以帮助避免在布尔变量中存储除"1"和"0"以外的值. (25认同)
  • 我认为使用(bool)明确地使用它会更清楚,为什么要使用这个棘手的!!,因为它的输入较少? (17认同)
  • 改善?血淋淋的地狱......给我一些你吸烟的东西. (17认同)
  • @lzprgmr:显式强制转换在MSVC上导致["性能警告"](http://msdn.microsoft.com/en-us/library/b6801kcy.aspx).使用`!!`或`!= 0`解决了这个问题,在这两个中我发现前者更清洁(因为它可以处理更多类型).此外,我同意没有理由在相关代码中使用任何一个. (6认同)
  • @Noldorin,我认为它*提高了*可读性 - 如果你知道它意味着什么,它是简单,整洁和合乎逻辑的. (6认同)
  • 完全正确,这是大多数语言中常见的习语!是一个带有隐式强制转换为布尔值的否定运算符 (3认同)
  • 我同意@baiyanhuang ...当然,它只会降低可读性而没有任何实际收益吗? (2认同)

Tom*_*rta 69

在某些情况下,它实际上是一个非常有用的习惯用语.拿这些宏(来自Linux内核的例子).对于海湾合作委员会,它们的实施如下:

#define likely(cond)   (__builtin_expect(!!(cond), 1))
#define unlikely(cond) (__builtin_expect(!!(cond), 0))
Run Code Online (Sandbox Code Playgroud)

他们为什么要这样做?GCC的__builtin_expect对待它的参数long,而不是bool,所以需要有某种形式的转换.由于他们不知道什么cond时候他们正在编写这些宏,所以最简单的就是使用这个!!成语.

他们可能通过与0比较来做同样的事情,但在我看来,做双重否定实际上更直接,因为那是最接近C的铸造到布尔的.

这段代码也可以在C++中使用......它是最常见的分母.如果可能的话,在C和C++中做什么都可以.


fiz*_*zer 50

编码器认为它会将操作数转换为bool,但由于&&的操作数已经隐式转换为bool,因此它完全是多余的.

  • 在没有这个技巧的情况下,Visual C++在某些情况下提供了性能. (13认同)
  • Visual C++ 2015继续提供性能警告. (2认同)

jwf*_*arn 11

是的,这是正确的,不,你不会错过任何东西. !!是对bool的转换.有关更多讨论,请参阅此问题.


Ric*_*son 11

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

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

守则必须清晰易读 - 否则你将为未来留下一段时间的债务遗产 - 因为需要时间来理解一些不必要的错综复杂的东西.

  • 这不是一个技巧,它是简单的类型转换. (8认同)
  • 我对一个技巧的定义是一开始并不是每个人都能理解的.需要弄清楚的东西是一个技巧.也是可怕因为!运营商可能超载...... (7认同)
  • @ orlandu63:简单的类型转换是`bool(expr)`:它做的是正确的,每个人都能理解第一眼的意图.`!!(expr)`是一个双重否定,意外转换为bool ......这并不简单. (5认同)

Rob*_*obH 9

它侧重于编译器警告.试试这个:

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

'bar'行在MSVC++上生成"强制值'bool'true'或'false'(性能警告)",但'baz'行悄然通过罚款.


Mar*_*cin 5

是运营商!超载?
如果没有,他们可能会这样做,将变量转换为bool而不产生警告.这绝对不是一种标准的做事方式.


Kar*_*rlU 5

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

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

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

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