在C++中,使用!!转换为bool是否安全?

55

[这个问题与这个问题相关,但并不相同。]

如果我尝试将某些类型的值用作布尔表达式,我会收到警告。而不是忽略警告,有时我会使用三元运算符(?:)将其转换为布尔值。使用两个非运算符(!!)似乎可以做同样的事情。

这是我的意思:

typedef long T;       // similar warning with void * or double
T t = 0;
bool b = t;           // performance warning: forcing 'long' value to 'bool'
b = t ? true : false; // ok
b = !!t;              // any different?

那么,“双非”技术真的能做同样的事情吗?它比三元表达式技术更安全还是不安全?对于非整型类型(例如,对于void *doubleT),这种技术是否同样安全?

我并不是在问!!t是否好看。我在问它是否和t ? true : false语义上有所不同。


3
当我看到像“b = t ? true : false;”这样的代码时,我总是想用“b = t”这一行代替它。因为已经有了将t隐式转换为bool类型的操作,为什么还要纠缠于语义呢? - Greg D
3
正如问题所述,因为 b=t 引发了一个警告。 - jwfearn
2
有趣的是,许多人基于主观因素反对 !!。在 JavaScript 中,双重否定是将变量转换为布尔值的惯用方式,如果 JavaScript 开发者能够理解它,我也期望 C++ 用户能够理解。 - just somebody
2
双重否定在大多数脚本语言中很常见。 https://dev59.com/nnRB5IYBdhLWcg3wv5tA 因此,在高水平的可理解代码(至少是编写者自己能读懂的)中,严格禁止使用它。 :) - vprajan
@GregD 一个情况是在结构体中为位域标志分配值时。你的解决方案会截断(至少在某些编译器中),而其他提供的解决方案会将标志设置为相关的真值。假设编译器会为你做正确的事情并不明智。将位域类型设置为布尔类型也无法改变这一点(至少在我处理的编译器中)。未定义的行为。 - Jostikas
@GregD b = t ? true : false; 具有将t显式转换为bool的显式强制类型转换。b = t; 具有隐式类型转换。尽管在必须明确时我更喜欢使用b = static_cast<bool>(t); - Caleth
18个回答

84

逻辑非运算符(!)的参数和三元运算符(?:)的第一个参数都会隐式转换为布尔类型,因此我认为 !! 和 ?: 是多余的语法糖。 我支持使用显式转换。

b = (t != 0);

禁止隐式转换。


42

或者,您可以这样做:bool b = (t != 0)

(注:该代码片段是关于it技术的)

4
我一直使用这个选项,因为我认为它最清楚地表达了意图。这实际上就是编译器在生成警告时所执行的操作,但是它更加明确。对于我来说,双感叹号似乎有些欺骗性。 - rmeador
如果t是一个提供了运算符(bool)的对象,那么这个方法就不起作用,而其他建议则可以。 - Jules

32

注意!

  • 布尔值是关于真与假的。
  • 整数是关于整数的概念。

这些都是非常不同的概念:

  • 真假是关于决定事情的概念。
  • 数字是关于计算事物的数量的概念。

当需要将这些概念联系起来时,应该明确地进行操作。我最喜欢Dima的版本:

b = (t != 0);

这段代码清楚地表明:比较两个数字并将真值存储在布尔型变量中。


7


是的,它是安全的。


0被解释为假,其他所有值都为真,
因此!5被解释为假
!0被解释为真
所以!!5被解释为真


6

所有有效的技巧都会生成相同的代码。

个人而言,我只是禁用了警告,这样我就可以使用最简洁的语法。将其转换为布尔值并不是我担心会意外执行的操作。


4
感谢Microsoft提供的C代码清晰、优雅的警告提示。 - Kristopher Johnson
我确实想知道是什么促使他们添加了这个警告;肯定有人做了一些非常愚蠢的事情... - Mike F
也许这会对某些目标架构产生影响?微软没有提供其他信息,只是警告普通强制转换和转换是“按设计”进行的。 - jwfearn
@MikeF 这个警告是为了让编译器“警告”你可能不打算做的事情。禁用警告可能会使代码“干净”,但可能会导致拼写错误或意外错误。(例如将 long 传递给需要 bool 的函数?) - moogs
@jwfearn,它说显式转换的警告与隐式转换“设计上”是相同的。警告存在是因为简单地强制转换为布尔值是不良的编程实践。使用bool b =(a!= 0)将其转换为布尔值可以准确地传达您的意图。强制转换为布尔值可能会避免担心类型问题,但会导致可维护性问题。 - iheanyi
显示剩余2条评论

6

我不建议使用以下内容:

bool b = !!t;

这是最不易读(因此最难维护)的方法。其他方法取决于具体情况。如果您只是将其转换为布尔表达式使用。
bool b = t ? true : false;
if (b)
{
    doSomething();
}

那么我会让编程语言为您完成:
if (t)
{
    doSomething();
}

如果你实际上是在存储布尔值。那么我首先会想知道为什么你需要对 long 进行强制转换。假设你需要长整型和布尔值,根据情况考虑以下所有内容。
bool  b = t ? true : false;      // Short and too the point.
                                 // But not everybody groks this especially beginners.
bool  b = (t != 0);              // Gives the exact meaning of what you want to do.
bool  b = static_cast<bool>(t);  // Implies that t has no semantic meaning
                                 // except as a bool in this context.

概述: 在特定的上下文中使用最有意义的内容。
尽量明确你正在做什么。


“让语言自己处理”(又称“常规转换”)是导致警告的原因。以下是可能需要使用此功能的示例:结构体C { void * hdl; void * get_hdl() { return hdl; } bool has_hdl() const { return hdl ? true : false; }} - jwfearn
7
易读性在读者的眼中。例如,我认为前缀运算符比后缀运算符更易读,简洁胜过冗长,干巴巴的代码胜过重复冗余的代码。双重否定是一个前缀运算符(优胜点),比三元运算符更短(优胜点),且可以被视为单个运算符,而三元运算符则包含三个不同的表达式(加上运算符语法)。 - just somebody

4
与0比较的效果不太好。 这就回到了一个问题——为什么要用!!而不是三元运算符?
class foo { public: explicit operator bool () ; };

foo f;

auto a = f != 0; // invalid operands to binary expression ('foo' and 'int')
auto b = f ? true : false; // ok
auto c = !!f; // ok

1
这个答案中有任何独特的新信息吗?如果你决定回答一个已经有了成熟和正确答案的老问题,那么在一天结束时再添加一个新答案可能不会给你带来任何好处。如果你有一些独特的新信息,或者你确信其他答案都是错误的,那么请添加一个新答案,但通常情况下,在问题被问出很久之后再提供相同基本信息的“又一个答案”通常不会为你赢得太多信用。 - Jonathan Leffler
1
是的,没有其他人意识到“!= 0”经常会失败。 - Jay K

3
我建议永远不要忽略该警告,也不要使用c cast(bool)来抑制它。转换可能并不总是按照您的假设进行调用。
一个求值为true的表达式和一个具有该值的布尔值之间存在区别。
!!和三元运算符都需要适应,但如果您不想使用重载的bool转换定义内部类型,则会执行类似的工作。
Dima的方法也很好,因为它将表达式的值分配给了一个布尔值。

你能举一个例子来说明那个警告会有帮助吗? - Mike F
可能在类型转换中你会丢失信息,也许你正在从一个不会在这种转换中丢失任何信息的环境中移植代码。如果是这种情况,你可能想知道有关丢失位的任何信息。 - wilhelmtell
1
当然你会失去信息。这就是转换为布尔值的全部意义所在! - John Millikin
@Mike:随着代码的更改,您要转换为布尔值的值可能会被typedef,因此生成的代码可能会发生变化。此外,有些编译器默认使用带符号和无符号字符,这可能会导致不同的结果。 - tabdamage
@tabdamage:你转换为bool的类型对于是否出现此警告没有影响,因此该示例无效。而char的符号与任何事情都无关。 - Mike F
显示剩余2条评论

2
如果您担心警告,也可以强制转换: bool b = (bool)t;

1
实际上,这与bool b = t;完全等价,并生成相同的警告。 - jwfearn
不要使用C语言的强制类型转换。C++中的类型转换是有原因的。此外,我没有在我的编译器上尝试过这个,但我不会期望它生成任何警告,因为你明确地要求编译器执行某些操作。编译器通常只在发生隐式操作时警告你。 - wilhelmtell
我在Ubuntu上这样做时,从未在GCC下看到警告。 - warren
Q不一定是VC++特定的(尽管那是我测试的地方),我正在询问!!x和x?true:false之间的差异(如果有的话)?我打赌许多编译器根据您的警告级别具有类似的警告。已删除visualstudio标记。 - jwfearn
@jwfeam:如果这是问题,那么你早就得到了答案(="不,没有区别")。顺便说一句,你会输掉关于其他编译器发出这个(相当奇怪的)警告的赌注。 - Mike F
显示剩余3条评论

2

我非常讨厌!!t!!!!!!. 它让人想起C和C++最糟糕的东西,即通过语法过于聪明。

bool b(t != 0); // 在我看来,这是最好的方式,它明确地显示了正在发生的事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接