C++ nullptr 究竟是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1282295/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:26:17  来源:igfitidea点击:

What exactly is nullptr?

c++pointersc++11nullptr

提问by AraK

We now have C++11 with many new features. An interesting and confusing one (at least for me) is the new nullptr.

我们现在拥有具有许多新功能的 C++11。一个有趣且令人困惑的(至少对我而言)是新的nullptr.

Well, no need anymore for the nasty macro NULL.

好吧,不再需要讨厌的宏了NULL

int* x = nullptr;
myclass* obj = nullptr;

Still, I am not getting how nullptrworks. For example, Wikipedia articlesays:

尽管如此,我还是不明白是如何nullptr工作的。例如,维基百科文章说:

C++11 corrects this by introducing a new keywordto serve as a distinguished null pointer constant: nullptr. It is of type nullptr_t, which is implicitly convertible and comparable to any pointer type or pointer-to-member type. It is not implicitly convertible or comparable to integral types, except for bool.

C++11 通过引入一个新的关键字作为一个特殊的空指针常量来纠正这个问题:nullptr。它的类型为 nullptr_t,它可以隐式转换并与任何指针类型或指向成员的指针类型相比较。除了 bool 之外,它不能隐式转换或与整型比较。

How is it a keyword and an instance of a type?

关键字和类型的实例如何?

Also, do you have another example (beside the Wikipedia one) where nullptris superior to good old 0?

另外,你还有另一个例子(在维基百科旁边)nullptr优于 good old0吗?

采纳答案by Johannes Schaub - litb

How is it a keyword and an instance of a type?

关键字和类型的实例如何?

This isn't surprising. Both trueand falseare keywords and as literals they have a type ( bool). nullptris a pointer literalof type std::nullptr_t, and it's a prvalue (you cannot take the address of it using &).

这并不奇怪。这两个truefalse是关键字和文字他们有一个类型(bool)。nullptr是一个类型为 的指针文字std::nullptr_t,它是一个纯右值(你不能使用它获取它的地址&)。

  • 4.10about pointer conversion says that a prvalue of type std::nullptr_tis 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 nullptrto select the pointer version. Passing NULLor 0would confusingly select the intversion.

  • A cast of nullptr_tto an integral type needs a reinterpret_cast, and has the same semantics as a cast of (void*)0to an integral type (mapping implementation defined). A reinterpret_castcannot convert nullptr_tto any pointer type. Rely on the implicit conversion if possible or use static_cast.

  • The Standard requires that sizeof(nullptr_t)be sizeof(void*).

  • 4.10关于指针转换说类型的纯右值std::nullptr_t是空指针常量,并且整型空指针常量可以转换为std::nullptr_t. 不允许相反方向。这允许为指针和整数重载函数,并传递nullptr以选择指针版本。通过NULL0会混淆地选择int版本。

  • 的铸造nullptr_t为整型需要reinterpret_cast,并且具有相同的语义的铸造(void*)0为整型(映射实现定义)。Areinterpret_cast不能转换nullptr_t为任何指针类型。如果可能,依靠隐式转换或使用static_cast.

  • 该标准要求sizeof(nullptr_t)sizeof(void*).

回答by nik

From nullptr: A Type-safe and Clear-Cut Null Pointer:

来自nullptr:类型安全且清晰的空指针

The new C++09 nullptr keyword designates an rvalue constant that serves as a universal null pointer literal, replacing the buggy and weakly-typed literal 0 and the infamous NULL macro. nullptr thus puts an end to more than 30 years of embarrassment, ambiguity, and bugs. The following sections present the nullptr facility and show how it can remedy the ailments of NULL and 0.

新的 C++09 nullptr 关键字指定了一个右值常量,用作通用空指针字面量,取代了错误和弱类型字面量 0 以及臭名昭著的 NULL 宏。因此,nullptr 结束了 30 多年的尴尬、模棱两可和错误。以下部分介绍了 nullptr 工具,并展示了它如何解决 NULL 和 0 的问题。

Other references:

其他参考:

回答by Gabriel Staples

Why nullptr in C++11? What is it? Why is NULL not sufficient?

为什么在 C++11 中使用 nullptr?它是什么?为什么 NULL 不够?

C++ expert Alex Allain says it perfectly here(my emphasis added in bold):

C++ 专家亚历克斯·阿兰 (Alex Allin) 在这里说得很好(我的重点用粗体表示):

...imagine you have the following two function declarations:

void func(int n); 
void func(char *s);

func( NULL ); // guess which function gets called?

Although it looks like the second function will be called--you are, after all, passing in what seems to be a pointer--it's really the first function that will be called! The trouble is that because NULL is 0, and 0 is an integer, the first version of func will be called instead.This is the kind of thing that, yes, doesn't happen all the time, but when it does happen, is extremely frustrating and confusing. If you didn't know the details of what is going on, it might well look like a compiler bug. A language feature that looks like a compiler bug is, well, not something you want.

Enter nullptr. In C++11, nullptr is a new keyword that can (and should!) be used to represent NULL pointers;in other words, wherever you were writing NULL before, you should use nullptr instead. It's no more clear to you, the programmer, (everyone knows what NULL means), but it's more explicit to the compiler, which will no longer see 0s everywhere being used to have special meaning when used as a pointer.

...假设您有以下两个函数声明:

void func(int n); 
void func(char *s);

func( NULL ); // guess which function gets called?

虽然看起来第二个函数会被调用——毕竟,你传入的是一个指针——但它实际上是第一个将被调用的函数!麻烦的是,因为NULL是0,而0是整数,所以会调用第一个版本的func。是的,这种事情不会一直发生,但一旦发生,就会非常令人沮丧和困惑。如果你不知道发生了什么的细节,它很可能看起来像一个编译器错误。看起来像编译器错误的语言功能不是您想要的。

输入 nullptr。在 C++11 中,nullptr 是一个新关键字,可以(并且应该!)用于表示 NULL 指针;换句话说,无论您之前在何处写入 NULL,都应该改用 nullptr。程序员你不太清楚,(每个人都知道 NULL 是什么意思),但它对编译器来说更明确,当用作指针时,它将不再在任何地方看到 0 具有特殊含义。

Allain ends his article with:

阿兰以这样的方式结束他的文章:

Regardless of all this--the rule of thumb for C++11 is simply to start using nullptrwhenever you would have otherwise used NULLin the past.

不管这一切——C++11 的经验法则是nullptr只要你NULL过去会使用,就开始使用。

(My words):

(我的话):

Lastly, don't forget that nullptris an object--a class. It can be used anywhere NULLwas used before, but if you need its type for some reason, it's type can be extracted with decltype(nullptr), or directly described as std::nullptr_t, which is simply a typedefof decltype(nullptr).

最后,不要忘记它nullptr是一个对象——一个类。它可以在任何地方使用NULL前使用,但是如果你需要它的类型出于某种原因,它的类型可以被提取decltype(nullptr),或直接描述为std::nullptr_t,这是一个简单的typedefdecltype(nullptr)

References:

参考:

  1. Cprogramming.com: Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint
  2. https://en.cppreference.com/w/cpp/language/decltype
  3. https://en.cppreference.com/w/cpp/types/nullptr_t
  1. Cprogramming.com:C++11 中更好的类型 - nullptr、枚举类(强类型枚举)和 cstdint
  2. https://en.cppreference.com/w/cpp/language/decltype
  3. https://en.cppreference.com/w/cpp/types/nullptr_t

回答by Motti

When you have a function that can receive pointers to more than one type, calling it with NULLis ambiguous. The way this is worked around now is very hacky by accepting an int and assuming it's NULL.

当您有一个可以接收指向多个类型的指针的函数时,用 with 调用它NULL是不明确的。通过接受一个 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); }
};

In C++11you would be able to overload on nullptr_tso that ptr<T> p(42);would be a compile-time error rather than a run-time assert.

C++11你将能够重载,nullptr_t所以这ptr<T> p(42);将是一个编译时错误而不是一个运行时错误assert

ptr(std::nullptr_t) : p_(nullptr)  {  }

回答by user633658

nullptrcan't be assigned to an integral type such as an intbut only a pointer type; either a built-in pointer type such as int *ptror a smart pointer such as std::shared_ptr<T>

nullptr不能分配给一个整数类型,例如 anint而只能分配一个指针类型;内置指针类型,例如int *ptr或智能指针,例如std::shared_ptr<T>

I believe this is an important distinction because NULLcan still be assigned to both an integral type and a pointer as NULLis a macro expanded to 0which can serve as both an initial value for an intas well as a pointer.

我相信这是一个重要的区别,因为NULL仍然可以分配给整数类型和指针,就像NULL扩展到的宏一样,0它既可以用作 an 的初始值,也可以用作int指针。

回答by Gabriel Schreiber

Also, do you have another example (beside the Wikipedia one) where nullptris superior to good old 0?

另外,您是否还有另一个示例(在维基百科旁边)nullptr优于 Good old 0 的示例?

Yes. It's also a (simplified) real-world example that occurred in our production code. It only stood out because gcc was able to issue a warning when crosscompiling to a platform with different register width (still not sure exactly why only when crosscompiling from x86_64 to x86, warns warning: converting to non-pointer type 'int' from NULL):

是的。这也是我们生产代码中发生的(简化的)现实世界示例。它之所以突出是因为 gcc 在交叉编译到具有不同寄存器宽度的平台时能够发出警告(仍然不确定为什么只有在从 x86_64 交叉编译到 x86 时,警告warning: converting to non-pointer type 'int' from NULL):

Consider this code (C++03):

考虑这个代码(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;
}

It yields this output:

它产生这个输出:

(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1

回答by Mark Rushakoff

Well, other languages have reserved words that are instances of types. Python, for instance:

好吧,其他语言都有作为类型实例的保留字。以 Python 为例:

>>> None = 5
  File "<stdin>", line 1
SyntaxError: assignment to None
>>> type(None)
<type 'NoneType'>

This is actually a fairly close comparison because Noneis typically used for something that hasn't been intialized, but at the same time comparisons such as None == 0are false.

这实际上是一个相当接近的比较,因为None通常用于尚未初始化的东西,但同时比较诸如None == 0false。

On the other hand, in plain C, NULL == 0would return true IIRC because NULLis just a macro returning 0, which is always an invalid address (AFAIK).

另一方面,在普通 C 中, NULL == 0将返回真正的 IIRC,因为NULL它只是一个返回 0 的宏,这始终是一个无效地址(AFAIK)。

回答by KTC

It is a keyword because the standard will specify it as such. ;-) According to the latest public draft (n2914)

它是一个关键字,因为标准将这样指定它。;-) 根据最新的公开草案 (n2914)

2.14.7 Pointer literals [lex.nullptr]

pointer-literal:
nullptr

The pointer literal is the keyword nullptr. It is an rvalue of type std::nullptr_t.

2.14.7 指针字面量 [lex.nullptr]

pointer-literal:
nullptr

指针文字是关键字nullptr。它是一个类型的右值std::nullptr_t

It's useful because it does not implicitly convert to an integral value.

它很有用,因为它不会隐式转换为整数值。

回答by Amit G.

Let's say that you have a function (f) which is overloaded to take both int and char*. Before C++ 11, If you wanted to call it with a null pointer, and you used NULL (i.e. the value 0), then you would call the one overloaded for int:

假设您有一个函数 (f),它被重载以接受 int 和 char*。在 C++ 11 之前,如果您想用空指针调用它,并且您使用了 NULL(即值 0),那么您将调用为 int 重载的那个:

void f(int);
void f(char*);

void g() 
{
  f(0); // Calls f(int).
  f(NULL); // Equals to f(0). Calls f(int).
}

This is probably not what you wanted. C++11 solves this with nullptr; Now you can write the following:

这可能不是您想要的。C++11 用 nullptr 解决了这个问题;现在您可以编写以下内容:

void g()
{
  f(nullptr); //calls f(char*)
}

回答by Vishal Chovatiya

Let me first give you an implementation of unsophisticated nullptr_t

先给大家介绍一个不成熟的实现 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;

nullptris a subtle example of Return Type Resolveridiom to automatically deduce a null pointer of the correct type depending upon the type of the instance it is assigning to.

nullptr返回类型解析器习语的一个微妙示例,它根据分配给的实例的类型自动推导出正确类型的空指针。

int *ptr = nullptr;                // OK
void (C::*method_ptr)() = nullptr; // OK
  • As you can above, when nullptris being assigned to an integer pointer, a inttype instantiation of the templatized conversion function is created. And same goes for method pointers too.
  • This way by leveraging template functionality, we are actually creating the appropriate type of null pointer every time we do, a new type assignment.
  • As nullptris an integer literal with value zero, you can not able to use its address which we accomplished by deleting & operator.
  • 如上所述,当nullptr被分配给整数指针时,int会创建模板化转换函数的类型实例化。方法指针也是如此。
  • 通过这种方式,通过利用模板功能,我们实际上每次都创建了适当类型的空指针,一个新的类型分配。
  • 由于nullptr是值为 0 的整数文字,您不能使用我们通过删除 & 运算符完成的地址。

Why do we need nullptrin the first place?

为什么我们nullptr首先需要?

  • You see traditional NULLhas some issue with it as below:
  • 您会看到传统NULL有一些问题,如下所示:

1???Implicit conversion

1???隐式转换

char *str = NULL; // Implicit conversion from void * to char *
int i = NULL;     // OK, but `i` is not pointer type

2???Function calling ambiguity

2???函数调用歧义

void func(int) {}
void func(int*){}
void func(bool){}

func(NULL);     // Which one to call?

  • Compilation produces the following error:
  • 编译产生以下错误:
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???Constructor overload

3???构造函数重载

struct String
{
    String(uint32_t)    {   /* size of string */    }
    String(const char*) {       /* string */        }
};

String s1( NULL );
String s2( 5 );

  • In such cases, you need explicit cast (i.e.,?String s((char*)0)).
  • 在这种情况下,您需要显式转换(即?String s((char*)0))