C ++ 中的 nullptr 到底是什么

“在C++中nullptr到底是什么?”这个问题的答案对于有经验的C++爱好者和了解现代C++的人来说是小菜一碟。 但是nullptr不仅是C ++中的关键字,而且为了解释这一点,我已经写了这篇文章。 但是在进入之前,我们将看到NULL问题,然后我们将深入研究nullptr的简单实现以及nullptr的一些用例。

/!\:最初发布于@ www.vishalchovatiya.com 。

注意:本文的内容大致相同,您可以在此处 , 此处和nullptr提案(N2431)中找到,但要有条理和简化。

为什么我们需要nullptr?

区分整数0(零)(即NULL)和类型指针的实际null。

nullptr与NULL

  • NULL为0(零),即,将C样式类型转换为void *的整数常数为零 ,而nullptr是nullptr_t类型的prvalue ,该值是整数文字,其 值为零
  • 对于那些相信NULL相同的人,即C和C ++中的(void *)0。 我想澄清的是,不是:

NULL-cppreference.com (C)

NULL-cppreference.com (C ++)

  • C ++要求将宏NULL定义为值为0的整数常量表达式。因此,与C语言不同,在C ++标准库中不能将NULL定义为(void *)0。

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))。

简单的nullptr的实现

  • nullptr是“ 返回类型解析器”惯用语的一个细微示例,可以根据分配给它的实例的类型自动推断出正确类型的空指针。
  • 考虑以下最简单且不复杂的nullptr实现:
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 ;
  • 如果上面的代码对您来说似乎很奇怪和怪异(尽管应该不会),那么我建议您阅读我之前有关高级C ++概念的文章。 这里的魔力只是模板化转换运算符。
  • 如果您有更权威的资料,那么这里是LLVM header中nullptr的具体实现 。

nullptr的用例

struct C { void func () ; };

int main ( void )
 {
    int *ptr = nullptr ;                // OK
    void (C::*method_ptr)() = nullptr ; // OK
    nullptr_t n1, n2;
    n1 = n2;
    //nullptr_t *null = &n1;           // Address can't be taken.
}
  • 如上例所示,当将nullptr分配给整数指针时,将创建模板化转换函数的int类型实例。 方法指针也是如此。
  • 这样,通过利用模板功能,实际上每次执行操作时,我们实际上都在创建适当的空指针类型,即新的类型分配。
  • 由于nullptr是值为零的整数文字 ,因此您无法使用通过删除&运算符完成的地址。

1️⃣函数使用nullptr调用清晰度

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

func( nullptr );
  • 现在,func(int *)将被调用,因为nullptr将隐式推导为int *。

2️⃣在nullptr_t上进行类型转换

  • 将nullptr_t强制转换为整数类型需要reinterpret_cast,并且具有与(void *)0强制转换为整数类型相同的语义。
  • 只要目标类型足够大,就将nullptr_t强制转换为整数类型。 考虑一下:
// int ptr_not_ok = reinterpret_cast(nullptr); // Not OK
long ptr_ok = reinterpret_cast < long long >( nullptr ); // OK
  • reinterpret_cast无法将nullptr_t转换为任何指针类型。 请改用static_cast。
void func ( int *)     { /*...*/ }
void func ( double *)  { /*...*/ }

func( nullptr );                            // compilation error, ambiguous call!

// func(reinterpret_cast(nullptr)); // error: invalid cast from type 'std::nullptr_t' to type 'int*'
func( static_cast < int *>( nullptr ));         // OK
  • nullptr可以隐式转换为任何指针类型,因此使用static_cast进行显式转换仅有效。

3️⃣nullptr_t可比

int *ptr = nullptr ;
if (ptr == 0 );          // OK
if (ptr <= nullptr );    // OK        

int a = 0 ;
if (a == nullptr );      // error: invalid operands of types 'int' and 'std::nullptr_t' to binary 'operator=='

来自Wikipedia :-…空指针常量:nullptr。 它的类型为nullptr_t,它可以隐式转换,并且可以与任何指针类型或指针到成员类型进行比较。
-除了布尔值,它不能隐式转换或与整数类型媲美。

const int a = 0 ;
if (a == nullptr ); // OK

const int b = 5 ;
if (b == nullptr ); // error: invalid operands of types 'const int' and 'std::nullptr_t' to binary 'operator=='

4️⃣模板参数的类型为std :: nullptr_t

template < typename T>
void ptr_func (T *t)  {}

ptr_func( nullptr );         // Can not deduce T
  • 如前所述, Return Type Resolver需要一个受让人来推断类型。
template < typename T>
void val_func (T t)  {}

val_func( nullptr );         // deduces T = nullptr_t
val_func(( int *) nullptr );   // deduces T = int*, prefer static_cast though

5️⃣从nullptr_t转换为bool

来自cppreference :
-在直接初始化的上下文中,可以从类型为std :: nullptr_t的prvalue初始化bool对象,包括nullptr。 结果值为false。 但是,这不被视为隐式转换。

  • 转换仅允许直接初始化 ,而不允许复制初始化 ,包括通过值将参数传递给函数的情况。 例如
bool b1 = nullptr ; // Not OK
bool b2 { nullptr }; // OK

void func ( bool ) {}

func( nullptr );     // Not OK, need to do func(static_cast(nullptr));

6️⃣杂项

typeid ( nullptr );                            // OK
throw nullptr ;                              // OK
char *ptr = expr ? nullptr : nullptr ;       // OK
// char *ptr1 = expr ? 0 : nullptr;         // Not OK, types are not compatible
static_assert ( sizeof ( NULL ) == sizeof ( nullptr_t ));

常见问题汇总

何时引入nullptr?

  • C ++ 11

nullptr是关键字还是std :: nullptr_t类型的实例?

  • true和false都是关键字和文字,因为它们都有一个类型(bool)。 nullptr是类型为std :: nullptr_t的指针文字 ,它是一个prvalue(即纯rvalue ,您不能使用&来获取它的地址)。 更多 。

使用nullptr有什么优势?

  • 重载集之间没有函数调用歧义。
  • 您可以使用nullptr_t进行模板专门化。
  • 代码将变得更加安全,直观和富有表现力。 如果(ptr == nullptr); 而不是if(ptr == 0);。

C ++中的NULL是否等于C ++ 11中的nullptr?

cout <<< endl ;
  • 一点也不。 以下行甚至不编译:

我可以将nullptr转换为bool吗?

  • 是。 但仅当您直接初始化时 。 即bool is_false {nullptr};。 否则需要使用static_cast。

如何定义nullptr?

  • 它只是被称为Return Type Resolver的模板化转换运算符。

原文链接: https://hackernoon.com/what-exactly-is-nullptr-in-c-94d63y6t

你可能感兴趣的:(C ++ 中的 nullptr 到底是什么)