为什么函数模板不理解NULL,但支持nullptr?

6

我有一个函数

int f(std::shared_ptr<MyClass> sptr);

之后我编写了以下模板以便调用它(和其他一些)函数:

template <typename Func, typename ArgType>
auto call(Func func, ArgType arg) -> decltype(func(arg))
{
    return func(arg);
}

当我尝试使用NULL和这个模板时,为什么第三行会出现错误?
auto r0 = f(0); // OK
auto r1 = call(f, nullptr); // OK
auto r2 = call(f, NULL); // ERROR! WHY??

1>------ Build started: Project: ConsoleApplication1, Configuration: Debug x64 ------
1>  main.cpp
1>main.cpp(245): error C2893: Failed to specialize function template 'unknown-type call(Func,Arg)'
1>          With the following template arguments:
1>          'Func=int (__cdecl *)(std::shared_ptr<MyClass>)'
1>          'Arg=int'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

f(NULL) 可以编译通过吗? - Paul R
@PaulR 是的 - 请看他问题中的 r0 - Barry
@Barry:那是 f(0) - 我是在问 f(NULL),以防万一NULL被定义为和 0 不同的东西。 - Paul R
@PaulR f(0)或f(NULL)都可以,因为0是特殊的。但当0的类型被推断为int时,从模板类型中推断出问题。其余操作人员可以进行实验并告知。 - Mohit Jain
@MohitJain:谢谢,是的,我刚刚读了Joachim在下面的解释 - 现在很清楚了。 - Paul R
2个回答

9
这里是关键线索:
Arg=int

NULL 必须是一个空指针常量,在 C++11 之前意味着它必须是一个整数常量,其值为零。在您的实现中,它具有 int 类型,并且很可能是字面值 0

因此,模板参数被推导为 int,而该类型无法转换为 shared_ptr,因此出现错误。

至于其他问题:

auto r0 = f(0); // OK

文字中的字面值0可以被视为空指针常量,并且如果直接传递给函数,则可以转换为shared_ptr。在调用中,参数不是字面值,而是int类型的变量,不能被转换。
auto r1 = call(f, nullptr); // OK

nullptr有其自身的类型,可转换为shared_ptr


但是为什么 f(0) 能够工作呢?我的意思是为什么转换在第一行中起作用,而在第三行中不起作用?在第一种情况下,shared_ptr 可以从 0 构造,在第三种情况下则不能。 - Serebrov
非常感谢!我明白了。 - Serebrov

3
因为在C++中,NULL通常被定义为0,它是一个int类型。所以模板类型ArgType被推导为int类型,而你不能将一个int类型转换为std::shared_ptr类型。
需要注意的是,对于例如f(0)的情况可以正常工作(并且应该适用于f(NULL)),但那是因为编译器知道在这种情况下0是一个空指针。 在call函数中,编译器不知道变量arg将具有什么值,只知道它的类型是int类型,不能隐式转换为一个指针类型。

2
是的,f(0); 是可以的。但是 int i = 0; f(i); 就会出错。 - Mohit Jain

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