将 NULL 与 nullptr 传递给模板参数有什么区别?

问题描述 投票:0回答:2

我正在处理一个相当统一的供应商提供的 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
替换两个
nullptr
可以解决问题,但为什么这很重要?

很可能,预处理器将

NULL
变成
0x0
甚至
0
,但最初的调用从未引起“注意”。使用 NULL 非常适合:

handlepath(handle, path, NULL, 0, coll, NULL);

当在包装器中使用时,为什么它(对于某些编译器)是一个问题?


更新:我的 FreeBSD 系统上的

/usr/include/sys/_null.h
有以下代码:

#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

所以:

  1. 对于 C,

    NULL
    (void *)0

  2. 对于 clang++ 来说

    NULL
    nullptr
    是同一件事,而对于 GNU 来说可能不是......

c++ templates c++17
2个回答
7
投票

在许多实现中,

NULL
只是
#define
,表示整数文字
0
,例如:

#define NULL 0

您可以直接将 literal

0
分配给任何指针,这就是为什么将
NULL
直接传递给目标函数效果很好。

由于您将

NULL
传递给模板参数,因此该参数将被推导为类型
int
,如错误消息所示。 要将
int
变量分配给指针,您需要显式类型转换。

nullptr
nullptr_t
类型,也可以直接赋值给任何指针。
nullptr_t
只有 1 个可能的值。因此,当将
nullptr
传递给模板参数时,该参数将被推导为类型
nullptr_t
。并且编译器知道如何将
nullptr_t
分配给指针。

因此,在调用模板时,您应该使用

nullptr
,但是如果您想使用
NULL
,那么您必须将其类型转换为
(char*)NULL
(或等效项),或者参数的任何其他指针类型期待。


1
投票

NULL
是一个宏,可以定义为
nullptr
或零值整数文字(例如
0
0L
)。哪一个是实现定义的,但因为只有后一种选择在 C 和 C++11 之前版本中有效,所以看到后者的机会很大。

nullptr
nullptr_t
的对象。这种类型的任何表达式始终是所谓的“空指针常量”,这意味着它可以隐式转换为任何指针类型的空指针值。 然而,零值整数表达式并不总是空指针常量。具体来说,only

空值整数

literals是可以转换为指针类型的空指针常量。 因此,如果将 0 直接传递给期望 char*

运行良好的函数,则文字

0

 是一个空指针常量,可以隐式转换为指针类型,从而产生空指针值.
但是您将文字作为整数类型传递给 
awrap
(通过推导)。当您在 
func(args...)

中使用参数时,它仍然是一个值为零的整数表达式,但它不是一个

整数文字

。因此,参数
不是
空指针常量,并且不能隐式转换为
char* 因此,您无法通过包装器传递指针参数 0
,因为您必须准确命名应该应用转换的文字。是否可以使用 
NULL

是由实现定义的。如果

NULL

 恰好被定义为 
nullptr
,则它有效,但如果它是零值整数文字,则无效。
这是一个很好的例子,说明为什么只应该使用 
nullptr
。零值整数文字的隐式转换行为仅因从 C 继承的历史原因而存在。如果该语言是今天设计的,我怀疑有人会在转换中添加如此奇怪的特殊情况。通常,可能的转换由表达式的类型和值类别确定。这是值和语法结构影响转换行为的唯一特殊情况。

© www.soinside.com 2019 - 2024. All rights reserved.