条款八 倾向使用nullptr而非0和NULL
简介
在C++中的字面量0
是一个int
,当C++在一个只可以使用指针的情景中找到0,它勉强地把其解释为null
指针。
对于NULL
也有类似的问题,具体实现允许给NULL一个整型(不一定是int,也可以是long等)。
但是最大的问题是0与NULL都不是指针类型
在C++98
中,主要的问题是在指针和整形的重载中会导致困惑
// 传递0或者NULL永远不会调用指针的重载函数
void f(int);
void f(bool);
void f(void*);
f(0); // call f(int)
f(NULL); // may not compile, typically calls f(int)
调用的主要问题在于,代码的表面意思(函数)与实际意思(整形)相互矛盾。
nullptr既不是一个整型,也不是一个指针类型,你可以把它看做一个指向
所有类型
的指针,其真正的类型是std::nullptr_t
,它能隐性转换为所有的指针类型。
案例
以下是调用锁的一段程序,且能正确运行,但是有些瑕疵:
int f1(std::shared_ptr<Widget> spw);
double f2(std::unique_ptr<Widget> upw);
bool f3(Widget* pw);
std::mutex f1m, f2m, f3m;
using MuxGuard = 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);
}
可以改写为模板调用方式:
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); // error!
auto result2 = lockAndCall(f2, f2m, NULL); // error!
auto result3 = lockAndCall(f3, f3m, nullptr); // fine
在第一个调用中,由于0
被推断为int
类型,而把int
当作std::unique_ptr<Widget>
类型传递时发生错误。第二个调用类似。
总结
倾向使用nullptr而非0和NULL
避免整形与指针