我正在处理一个相当统一的供应商提供的 API,并且希望以统一的方式检查和处理任何故障。为此,我编写了以下包装器:
template <typename Func, typename... Args>
auto awrap(Func &func, Args&&... args)
{
auto code = func(args...);
if (code >= 0)
return code;
... handle the error ...
};
...
awrap(handlepath, handle, path, NULL, 0, coll, NULL);
上面的代码可以通过 clang 编译,但是 g++13 和 Microsoft VC++ 都对两个 NULL 参数提出抱怨:
... error: invalid conversion from 'int' to 'const char*' [-fpermissive]
87 | auto code = func(args...
用 替换两个NULL
s 可以nullptr
解决问题,但是这有什么关系呢?
很可能,预处理器NULL
会将 转换为0x0
,甚至0
在某个地方,但原始调用从未引起“注意”。在以下情况下,使用 NULL 非常合适:
handlepath(handle, path, NULL, 0, coll, NULL);
为什么在包装器中使用时会出现问题(对于某些编译器而言)?
更新:/usr/include/sys/_null.h
我的 FreeBSD 系统上有以下代码:
#ifndef NULL
#if !defined(__cplusplus)
#define NULL ((void *)0)
#else
#if __cplusplus >= 201103L
#define NULL nullptr
#elif defined(__GNUG__) && defined(__GNUC__) && __GNUC__ >= 4
#define NULL __null
#else
#if defined(__LP64__)
#define NULL (0L)
#else
#define NULL 0
#endif /* __LP64__ */
#endif /* __GNUG__ */
#endif /* !__cplusplus */
#endif
所以:
对于 C,
NULL
是(void *)0
;对于 clang++ 来说
NULL
和nullptr
是同一件事,而对于 GNU 来说可能不是......
在许多实现中,
NULL
仅仅是#define
整数字面量的0
,例如:您可以直接将文字
0
分配给任何指针,这就是NULL
直接传递给目标函数可以正常工作的原因。由于您要传递
NULL
给模板参数,因此该参数将被推断为类型int
,正如错误消息所示。在此调用中:
awrap()
将解析为如下内容:要将
int
变量分配给指针,您需要进行显式类型转换,但您并未使用它。而
nullptr
是 类型nullptr_t
,也可以直接赋值给任何指针。 只有一个可能的值nullptr_t
。因此,当传递nullptr
给模板参数时,该参数将被推断为 类型nullptr_t
。并且编译器知道如何将 赋值nullptr_t
给指针。在此调用中:
awrap()
将解析为如下内容:因此,当调用模板时,您应该使用
nullptr
。但是,如果您想使用,
NULL
那么您必须将其类型转换为(char*)NULL
(或等效类型),或参数期望的任何其他指针类型,例如:NULL
是一个宏,可以定义为nullptr
或 为零值整数文字(例如0
或0L
)。哪一个是实现定义的,但由于只有后者在 C 和 C++11 之前有效,因此看到后者的机会很大。nullptr
是 的对象nullptr_t
。任何此类型的表达式始终是所谓的空指针常量,这意味着它可以隐式转换为任何指针类型的空指针值。然而,零值整数表达式并不总是空指针常量。具体来说,只有空值整数文字才是可以转换为指针类型的空指针常量。
因此,如果直接传递给期望正常运行的
0
函数,则文字是空指针常量,并且可以隐式转换为指针类型,从而产生空指针值。char*
0
但是您将文字
awrap
作为整数类型传递给 (通过推导)。当您使用参数时,func(args...)
它仍然是一个值为零的整数表达式,但它不是整数文字。因此,参数不是空指针常量,不能隐式转换为char*
。因此,您无法
0
通过包装器传递指针参数,因为您必须准确命名应应用转换的文字。并且是否可以使用NULL
是实现定义的。如果NULL
恰好定义为,它可以工作nullptr
,但如果它是零值整数文字则不行。这是一个很好的例子,说明为什么只
nullptr
应使用。零值整数文字的隐式转换行为仅因从 C 继承的历史原因而存在。如果该语言是今天设计的,我怀疑是否有人会在转换中添加如此奇怪的特殊情况。通常,可能的转换由表达式的类型和值类别决定。这是值和语法结构影响转换行为的唯一特殊情况。