我想了解这个断言宏

I'd like to understand this assert macro

在Visual Studio中,断言宏定义如下:

#define assert(expression) (void)(                                                       \
            (!!(expression)) ||                                                              \
            (_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
        )

我在 || 之后得到了正确的部分仅在左侧表达式为假时才求值,并抛出异常。

我有 3 个问题:

  1. !!双重否定是把'expression'类型转成bool吧?如果他们被排除在外会发生什么? '表达式在与另一个表达式进行逻辑或运算时会隐式转换为布尔值,对吗?在:

    浮动()|| /* 调用抛出的函数*/;

float() 无论如何都转换为 bool 类型?

2.After ||我们有运算符:

 ( /* call function that throws */  , 0 )

逗号是一个序列点,因此首先调用函数,然后将此表达式计算为 0。如果函数调用没有终止程序,则表达式将简单地为:

!!(expression)) || 0

逗号后面的 0 有什么意义?如果不存在,则表达式为:

!!(expression)) || / * call function that throws */

这是一回事吧?

  1. 最后是 (void),我想知道这是干什么用的。

让我们分解一下代码:

#define assert(expression) (void)(                                                       \
            (!!(expression)) ||                                                              \
            (_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
        )

(void)是为了消除编译器警告表达式结果未被使用。并且还确保断言不会被意外用作更大表达式的一部分。

!!(expression)!! 部分是为了确保表达式的计算结果为 falsetrue.

但是恶作剧的 C++ 代码可能会覆盖 operator! 并在此处造成一些混乱。

为什么是 C 风格的转换,而不是 static_cast<bool>?我怀疑同一个头文件用于支持 C 和 C++,也可能用于支持 C++/CLI。 (如果是这样,可能有理由在两种语言特定的宏中对 C++ 使用 static_cast<bool>,对 C 使用 (bool)。)

如果 expression 对象有一个 operator bool 转换,它将被 !! 使用。 (但也会被 (bool)(expression) 使用。)

无论如何,!! 不必要的观察对我来说似乎是正确的。 C++ 中 truthy(非零)或 falsy(零)的表达式是语言的一部分。

使用 (x(), 0) 逗号运算符是因为 x(在本例中为 _wassert)returns 无效。如果 b 为空,则不能执行 a || b,所以这个技巧只是为了调用 void,并且表达式导致 int。没关系是0.

_wassert不抛出。它可能会以退出代码 3 中止。它可能会进入调试器。

可以忽略并继续。如果断言被违反,可能会导致断轴后果,或者代码可能有一些防御性编程来处理断言违规。

由于 assert 类函数宏被编写为 表达式,它巧妙地避免了“悬空 if 宏”的问题(有一个 if (!(expression)) ... 构造被使用),并且是 表达式 而不是 语句 .