定义双感叹号?

18

我知道双感叹号的作用(或者我认为我知道),但我不确定如何在一个随机对象上定义它。例如,在下面的代码片段中:

Assignment *a;
if (!getAssignment(query, a))
   return false;
hasSolution = !!a;

if (!a)
   return true;
我该如何知道双重感叹号会产生什么值?换句话说,它是否总是转换为true?false?还是你可以定义一个行为来确定结果(对象如何知道在这种情况下如何处理)?由于所有这些感叹号的事情让我有点困惑,对这段代码的任何解释都将不胜感激。希望我表述清楚了,谢谢。

4
在C++中,!!是一种反模式,就是这样。 - Konrad Rudolph
@Konrad:我不同意——这不是反模式,而是一种只允许重载operator!而不是operator!operator bool(或某些安全布尔习惯用法)的模式。 - ildjarn
1
@ildjarn 我认为这两者都是,如果你无法实现布尔运算符(尤其是在C++11中,但即使在之前),你应该完全避免操作符重载。对已经确立的惯用语的无知不能成为编写或支持糟糕代码的借口,抱歉。 - Konrad Rudolph
3
@Konrad:你说这是反模式,我说这是一种独特的惯用法。你对“!!”的不喜欢并不能使它变得“糟糕”。想要避免VC++中的C4305警告吗?你可以使用static_cast或者!! -- 哪一个更易于输入和阅读呢? - ildjarn
5
你应该使用自解释的代码,不要使用强制类型转换或 !! 这样的隐式强制转换,而应该明确地进行比较(在最后一种情况下,这是弱类型)。使用 a != nullptr - Konrad Rudolph
显示剩余2条评论
7个回答

24

a是一个指针。在C++中,nullptr被定义为无效指针。!pointer将一个nullptr指针转换为true,将非nullptr指针转换为false!booleantrue转换为false,将false转换为true。它总是起作用。

!(!a)是一种有用的思考方式。


我已将0更改为NULL,以避免一些常见的混淆,即NULL指针必须具有二进制值0(实际上并非如此,0整数常量可以隐式转换为NULL指针,但是NULL的表示形式没有保证)。 - David Rodríguez - dribeas
NULL并没有被定义为无效指针,而是在逻辑上被定义为false。a = NULL; 是完全合法的。记住!!a最简单的方法是将a!=0缩小为1,将a==0缩小为0。 - rurban
在C++中,使用0作为规范无效指针是合法的,并且历史上被用作标准。NULL是C语言的产物,仅作为C标准库兼容性的一部分支持。不建议在C++中使用NULL。为避免不必要的争论,我将把它改为C++11中的关键字nullptr - Jeffery Thomas

11

不要把它看作是“双感叹号”,而应该将其视为两个单独的运算符,其中一个作用于另一个的结果。

对于所有基本类型,这种方法都可以“管用”。!a等同于a == 0,因此!!a等同于!(a == 0),进而等同于a != 0

对于用户自定义类型,除非它们重载了operator !,否则代码无法通过编译。但显然,在这种情况下,行为可能是几乎任何东西。


5

!!在C++中不是一个单独的标记,它只会将!操作符应用两次。

由于a是指针而不是类类型对象,因此无法重载!。它被定义为如果a是空指针则返回true,否则返回false

第二个!的应用只是对第一个!的结果取反。

表达式!!a等同于a != 0


5
代码非常复杂。实际上,您想要测试getAssigment方法是否成功,并且分配的指针是否为非空。该代码以错综复杂的方式进行测试,利用了弱类型,而不是尝试拥抱明确性和C++的强类型。因此,它不是惯用的C++语言,比必要的更难理解。
特别是,不要在C++中使用!!a。这是一种在弱类型语言(如JavaScript)中建立的习惯用法,用于将值强制转换为布尔类型。但在C++中,这不是常用的。
由于未定义或使用hasSolution,因此不清楚代码执行的内容。但是,我怀疑该代码应该等同于以下内容:
Assignment *a;
return getAssignment(query, a) and a == nullptr;

(在C++11之前,需要写0而不是nullptr。)
然而,这段代码仍然展现出糟糕的设计:为什么要通过引用传递a?为什么不是返回值?更糟糕的是,a从未被使用,因此是不必要的。如果a确实是不必要的,应该完全省略它。如果它是必要的,它应该成为返回值。换句话说,getAssignment的原型应该如下:
Assignment* getAssignment(the_type_of_query query);

它应该简单地按如下方式使用:

Assignment* a = getAssignment(query);

此外,我怀疑这段代码实际上将内存所有权分配给了裸指针 a。在现代C++中,这是强烈不建议的。要么不使用指针,要么使用智能指针。

0

没有"!!"运算符,因此实际上该语句相当于:

hasSolution = !(!a);

因此,首先在表达式"a"上调用operator!()运算符,然后在结果上再次调用operator!()。在我们的代码中,“a”是指向Assignement的指针。C++为在指针类型上使用operator!()定义了一个特殊情况:如果指针为空,则返回一个为真的布尔值,否则返回为假。简而言之,与表达式(a == 0)相同。对(!a)的结果进行operator!()调用,它是一个布尔值,只需颠倒结果,即如果(!a)为假,则返回真,反之亦然。

因此,(!!a)返回与(a != 0)相同的结果。这是因为"a"是一个指针。


0

记住双重否定 !!a 最简单的方法是将 a!=0 缩小为 1,将 a==0 缩小为 0。这在布尔上下文(即 C++)中是真还是假。


0
bool result = true; 
result = !!result; // result = true, e.g. !result is false, !!result is !false.

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