现在c++ 11有了许多新特性。一个有趣而令人困惑的(至少对我来说)是新的nullptr。
不需要讨厌的宏NULL了。
int* x = nullptr;
myclass* obj = nullptr;
不过,我还是不明白nullptr是如何工作的。例如,维基百科的一篇文章说:
c++ 11通过引入一个新的关键字作为区分空指针常量nullptr来纠正这一点。它的类型为nullptr_t,可隐式转换,可与任何指针类型或指针到成员类型相比较。它不能隐式转换,也不能与整型相比,bool类型除外。
它如何既是关键字又是类型的实例?
此外,你是否有另一个例子(除了维基百科的一个),其中nullptr优于好旧的0?
当一个函数可以接收指向多个类型的指针时,用NULL来调用它是不明确的。这种工作方式现在是非常hack的接受一个int并假设它是NULL。
template <class T>
class ptr {
T* p_;
public:
ptr(T* p) : p_(p) {}
template <class U>
ptr(U* u) : p_(dynamic_cast<T*>(u)) { }
// Without this ptr<T> p(NULL) would be ambiguous
ptr(int null) : p_(NULL) { assert(null == NULL); }
};
在c++ 11中,你可以重载nullptr_t,这样ptr<T> p(42);将是编译时错误,而不是运行时断言。
ptr(std::nullptr_t) : p_(nullptr) { }
此外,你是否有另一个例子(除了维基百科的一个),其中nullptr优于好旧的0?
是的。这也是在我们的生产代码中出现的一个(简化的)真实例子。它之所以突出,是因为gcc能够在交叉编译到具有不同寄存器宽度的平台时发出警告(仍然不确定为什么只有在从x86_64交叉编译到x86时,警告警告:从NULL转换为非指针类型'int'):
考虑以下代码(c++ 03):
#include <iostream>
struct B {};
struct A
{
operator B*() {return 0;}
operator bool() {return true;}
};
int main()
{
A a;
B* pb = 0;
typedef void* null_ptr_t;
null_ptr_t null = 0;
std::cout << "(a == pb): " << (a == pb) << std::endl;
std::cout << "(a == 0): " << (a == 0) << std::endl; // no warning
std::cout << "(a == NULL): " << (a == NULL) << std::endl; // warns sometimes
std::cout << "(a == null): " << (a == null) << std::endl;
}
它产生如下输出:
(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1
它如何既是关键字又是类型的实例?
这并不奇怪。true和false都是关键字,作为字面量,它们有一个类型(bool)。Nullptr是一个std::nullptr_t类型的指针字面值,它是一个prvalue(不能使用&获取它的地址)。
4.10 about pointer conversion says that a prvalue of type std::nullptr_t is a null pointer constant, and that an integral null pointer constant can be converted to std::nullptr_t. The opposite direction is not allowed. This allows overloading a function for both pointers and integers, and passing nullptr to select the pointer version. Passing NULL or 0 would confusingly select the int version.
A cast of nullptr_t to an integral type needs a reinterpret_cast, and has the same semantics as a cast of (void*)0 to an integral type (mapping implementation defined). A reinterpret_cast cannot convert nullptr_t to any pointer type. Rely on the implicit conversion if possible or use static_cast.
The Standard requires that sizeof(nullptr_t) be sizeof(void*).
当一个函数可以接收指向多个类型的指针时,用NULL来调用它是不明确的。这种工作方式现在是非常hack的接受一个int并假设它是NULL。
template <class T>
class ptr {
T* p_;
public:
ptr(T* p) : p_(p) {}
template <class U>
ptr(U* u) : p_(dynamic_cast<T*>(u)) { }
// Without this ptr<T> p(NULL) would be ambiguous
ptr(int null) : p_(NULL) { assert(null == NULL); }
};
在c++ 11中,你可以重载nullptr_t,这样ptr<T> p(42);将是编译时错误,而不是运行时断言。
ptr(std::nullptr_t) : p_(nullptr) { }
让我首先给您一个简单的nullptr_t的实现
struct nullptr_t
{
void operator&() const = delete; // Can't take address of nullptr
template<class T>
inline operator T*() const { return 0; }
template<class C, class T>
inline operator T C::*() const { return 0; }
};
nullptr_t nullptr;
nullptr是返回类型解析器习惯用法的一个微妙示例,它根据分配给实例的类型自动推导出正确类型的空指针。
int *ptr = nullptr; // OK
void (C::*method_ptr)() = nullptr; // OK
如上所述,当nullptr被赋值给整数指针时,将创建模板化转换函数的int类型实例化。方法指针也是一样。
通过这种方式利用模板功能,我们实际上每次都创建了适当类型的空指针,这是一个新的类型赋值。
因为nullptr是一个值为0的整型字面值,你不能使用它的地址,这是我们通过删除&操作符实现的。
为什么我们首先需要nullptr ?
你可以看到传统的NULL有一些问题,如下:
1️⃣隐式转换
char *str = NULL; // Implicit conversion from void * to char *
int i = NULL; // OK, but `i` is not pointer type
2️⃣函数调用歧义
void func(int) {}
void func(int*){}
void func(bool){}
func(NULL); // Which one to call?
编译会产生以下错误:
error: call to 'func' is ambiguous
func(NULL);
^~~~
note: candidate function void func(bool){}
^
note: candidate function void func(int*){}
^
note: candidate function void func(int){}
^
1 error generated.
compiler exit status 1
3️⃣构造函数重载
struct String
{
String(uint32_t) { /* size of string */ }
String(const char*) { /* string */ }
};
String s1( NULL );
String s2( 5 );
在这种情况下,需要显式强制转换(即String s((char*)0))。