字面常量是0的型别是int,而非指针。而NULL的型别不确定,但不具有指针型别。
这样的问题可以会出现在以下代码中:
void f(int);
void f(bool);
void f(void*);
f(0); //调用的是f(int),而不是f(void*)
f(NULL); //可能通不过编译,一般也会调用f(int),从不会调用f(void*)
f(nullptr); //调用的是f(void*)
实际上,指导原则是不要在指针型别和整型之间做重载。但如果有这样的重载,那么只有nullptr
是可以匹配上的。
nullptr
不具备整型型别,实际上,它也不具备指针型别,但它可以隐式转换到所有裸指针型别。
以上优点还并不是nullptr
的唯一优点,它还可以让代码更加清晰:
nullptr使代码更清晰
auto result = findRecord(/* 实参 */);
if (result == 0){
...
}
auto result = findRecord(/* 实参 */);
if (result == nullptr){
...
}
上述第一种写法会有多义性,导致无法明了result的型别,但是第二种写法一眼可以看出result是个指针。
更大的优势在于nullptr用于模板
int f1(std::shared_ptr<Widget> spw);
double f2(std::unique_ptr<Widget> upw);
bool f3(Widget* pw);
//调用示例
std::mutex f1m, f2m, f3m;
using MuxGurad = std::lock_guard<std::mutex>;
...
{
MuxGuard g(f1m);
auto result = f1(0);
}
...
{
MuxGuard g(f2m);
auto result = f2(NULL);
}
...
{
MuxGuard g(f3m);
auto result = f3(nullptr);
}
...
以上代码就能运作,这时候,看到重复的代码流程,我们想写模板的思想就产生出来了,一定要泛化一个呗。于是乎,来了如下的代码:
//C++11
template<typename FuncType, typename MuxType, typename PtrType>
auto lockAndCall(FuncType func, MuxType& mutex, PtrType ptr)->decltype(func(ptr))
{
MuxGuard g(mutex);
return func(ptr);
}
//C++14
template<typename FuncType, typename MuxType, typename PtrType>
decltype(auto) lockAndCall(FuncType func, MuxType& mutex, PtrType ptr)
{
MuxGuard g(mutex);
return func(ptr);
}
//调用示例:
auto result1 = lockAndCall(f1, f1m, 0); //错误!
auto result2 = lockAndCall(f2, f2m, NULL); //错误!
auto result3 = lockAndCall(f3, f3m, nullptr); //没问题!
result1
无法编译的问题是因为0
将PtrType
推导为int
,那么func对应的传入就为int,与f1期望的std::shared_ptr
不兼容。而result2
的错误与result1
类似。而由于nullptr
由于肯定不会被解析为int
从而免疫了这种问题。
要点速记 |
---|
1. 相对于0或NULL,优先选用nullptr。 |
2. 避免在整型和指针型别之间重载。 |