条款八 倾向使用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

  • 避免整形与指针

本文固定链接: http://www.js-code.com/cpp/cpp_59135.html