655

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

Well, no need anymore for the nasty macro NULL.

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

Still, I am not getting how nullptr works. For example, Wikipedia article says:

C++11 corrects this by introducing a new keyword to 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.

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

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

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
Khaled Alshaya
  • 94,250
  • 39
  • 176
  • 234
  • 28
    related fact: `nullptr` is also used to represent null reference for managed handles in C++/CLI. – Mehrdad Afshari Aug 15 '09 at 16:52
  • 3
    When using Visual C++, remember that if you use nullptr with native C/C++ code and then compile with the /clr compiler option, the compiler cannot determine whether nullptr indicates a native or managed null pointer value. To make your intention clear to the compiler, use nullptr to specify a managed value or __nullptr to specify a native value. Microsoft has implemented this as a component extension. – C. Sederqvist Oct 28 '13 at 23:41
  • 7
    Is `nullptr_t` guaranteed to have only one member, `nullptr`? So, if a function returned `nullptr_t`, then the compiler already knows which value will be returned, regardless of the body of the function? – Aaron McDaid Jul 28 '15 at 11:02
  • 9
    @AaronMcDaid `std::nullptr_t` can be instantiated, but all instances will be identical to `nullptr` because the type is defined as `typedef decltype(nullptr) nullptr_t`. I believe the primary reason the type exists is so that functions can be overloaded specifically to catch `nullptr`, if necessary. See [here](http://ideone.com/raJNP9) for an example. – Justin Time - Reinstate Monica Jun 14 '16 at 23:26
  • 8
    0 never was a null pointer, null pointer is a pointer that can be get by _casting_ zero literal to pointer type, and it doesn't point to _any_ existing object by definition. – Swift - Friday Pie Mar 06 '17 at 07:26
  • I never saw the point of having a NULL macro instead of just typing 0. – Nils Apr 25 '18 at 10:39
  • 7
    @Nils The point is expressing intent! – curiousguy Jul 08 '19 at 01:06

14 Answers14

444

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

This isn't surprising. Both true and false are keywords and as literals they have a type ( bool ). nullptr is a pointer literal of type std::nullptr_t, and it's a prvalue (you cannot take the address of it using &).

  • 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*).

Mankarse
  • 39,818
  • 11
  • 97
  • 141
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1
    Oh, after looking, it seems to me that the conditional operator can't convert 0 to nullptr in cases like `cond ? nullptr : 0;`. Removed from my answer. – Johannes Schaub - litb Aug 15 '09 at 17:23
  • 100
    Note that `NULL` is not even guaranteed to be `0`. It can be `0L`, in which case a call to `void f(int); void f(char *);` will be ambiguous. `nullptr` will always favor the pointer version, and never call the `int` one. Also note that `nullptr` *is* convertible to `bool` (the draft says that at `4.12`). – Johannes Schaub - litb Aug 15 '09 at 18:09
  • @litb: so regarding f(int) and f(void*) - will f(0) still be ambiguous? – Steve Folly Aug 15 '09 at 20:48
  • 28
    @Steve, no that will call the `int` version. But `f(0L)` is ambiguous, because `long -> int` aswell as `long -> void*` is both equally costly. So if NULL is `0L` on your compiler, then a call `f(NULL)` will be ambiguous given those two functions. Not so with `nullptr` of course. – Johannes Schaub - litb Aug 15 '09 at 22:14
  • The conversion of nullptr to bool in 4.12 only applies to direct-initialization, so if(nullptr) is not covered. – Rastaban Jan 22 '14 at 20:55
  • **Will** it, or **can** it call the `int` overload instead of the pointer overload? – Deduplicator Jan 14 '15 at 20:20
  • **Will**. If you do `f(0)`, it will of course call `f(int)`, as `0` is an integer. How will the compiler know you wanted the pointer version? Note that NULL _usually_ is not defined as `(void*)0` but simply as `0` or `0L` – SvenS Jun 26 '15 at 12:58
  • 2
    @SvenS It must not be defined as `(void*)0` in C++. But it can be defined as any arbitrary null pointer constant, which any integral constant with value 0 and `nullptr` fulfill. So, most definitely not *will* but *can*. (You forgot to ping me btw..) – Deduplicator Feb 24 '16 at 01:31
113

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

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

...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.

Allain ends his article with:

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

(My words):

Lastly, don't forget that nullptr is an object--a class. It can be used anywhere NULL was 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 typedef of decltype(nullptr), as shown here:

Defined in header <cstddef>:

See:

  1. https://en.cppreference.com/w/cpp/types/nullptr_t
  2. and https://en.cppreference.com/w/cpp/header/cstddef
namespace std
{
typedef decltype(nullptr) nullptr_t; // (since C++11)
// OR (same thing, but using the C++ keyword `using` instead of the C and C++ 
// keyword `typedef`):
using nullptr_t = decltype(nullptr); // (since C++11)
} // namespace std

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
  4. https://en.cppreference.com/w/cpp/header/cstddef
  5. https://en.cppreference.com/w/cpp/keyword/using
  6. https://en.cppreference.com/w/cpp/keyword/typedef
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
64

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

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.

Other references:

Community
  • 1
  • 1
nik
  • 13,254
  • 3
  • 41
  • 57
  • 18
    C++09? Wasn't it referred to as C++0x before Aug 2011? – Michael Dorst Mar 12 '13 at 19:10
  • 3
    @anthropomorphic Well that's its purpose. C++0x was used while it was still work in progress, because it wasn't known whether it would be finished 2008 or 2009. Note that it actually became C++0B meaning C++11. See http://www.stroustrup.com/C++11FAQ.html – mxmlnkn Apr 14 '16 at 09:59
37

When you have a function that can receive pointers to more than one type, calling it with NULL is ambiguous. The way this is worked around now is very hacky by accepting an int and assuming it's 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++11 you would be able to overload on nullptr_t so that ptr<T> p(42); would be a compile-time error rather than a run-time assert.

ptr(std::nullptr_t) : p_(nullptr)  {  }
Motti
  • 110,860
  • 49
  • 189
  • 262
10

nullptr can't be assigned to an integral type such as an int but only a pointer type; either a built-in pointer type such as int *ptr or a smart pointer such as std::shared_ptr<T>

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

L. F.
  • 19,445
  • 8
  • 48
  • 82
user633658
  • 2,463
  • 2
  • 18
  • 16
6

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

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

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

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

Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • 5
    `NULL` is a macro that expands to a zero, a constant zero cast to a pointer produces a null pointer. A null pointer doesn't have to be zero (but often is), zero isn't always an invalid address, and a non-constant zero cast to a pointer doesn't have to be null, and a null pointer cast to an integer doesn't have to be zero. I hope I got that all right without forgetting anything. A reference: http://c-faq.com/null/null2.html – Samuel Edwin Ward Mar 02 '13 at 01:19
6

Also, do you have another example (beside the Wikipedia one) where nullptr is superior to 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):

Consider this code (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
the swine
  • 10,713
  • 7
  • 58
  • 100
Gabriel Schreiber
  • 2,166
  • 1
  • 20
  • 33
  • I fail to see how this improves when using nullptr (and C++11). If you set pb to nullptr the first comparison evaluates still true (while comparing apples with pears..). The second case is even worse: If you compare a to nullptr it will convert a to B* and then it will evaluate to true again (before it was casted to bool and the expr evaluated to false). The whole thingy reminds me of JavaScript and I wonder if we will get === in C++ in the future :( – Nils Apr 25 '18 at 11:01
4

Let me first give you an implementation of unsophisticated 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 is a subtle example of Return Type Resolver idiom to automatically deduce a null pointer of the correct type depending upon the type of the instance it is assigning to.

int *ptr = nullptr;                // OK
void (C::*method_ptr)() = nullptr; // OK
  • As you can above, when nullptr is being assigned to an integer pointer, a int type 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 nullptr is an integer literal with value zero, you can not able to use its address which we accomplished by deleting & operator.

Why do we need nullptr in the first place?

  • You see traditional NULL has some issue with it as below:

1️⃣ Implicit conversion

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

2️⃣ Function calling ambiguity

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

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)).
Yun
  • 3,056
  • 6
  • 9
  • 28
3

It is a keyword because the standard will specify it as such. ;-) According to the latest public draft (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.

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

Community
  • 1
  • 1
KTC
  • 8,967
  • 5
  • 33
  • 38
2

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:

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:

void g()
{
  f(nullptr); //calls f(char*)
}
Amit G.
  • 2,546
  • 2
  • 22
  • 30
0

0 used to be the only integer value that could be used as a cast-free initializer for pointers: you can not initialize pointers with other integer values without a cast. You can consider 0 as a consexpr singleton syntactically similar to an integer literal. It can initiate any pointer or integer. But surprisingly, you'll find that it has no distinct type: it is an int. So how come 0 can initialize pointers and 1 cannot? A practical answer was we need a means of defining pointer null value and direct implicit conversion of int to a pointer is error-prone. Thus 0 became a real freak weirdo beast out of the prehistoric era. nullptr was proposed to be a real singleton constexpr representation of null value to initialize pointers. It can not be used to directly initialize integers and eliminates ambiguities involved with defining NULL in terms of 0. nullptr could be defined as a library using std syntax but semantically looked to be a missing core component. NULL is now deprecated in favor of nullptr, unless some library decides to define it as nullptr.

Yoon5oo
  • 496
  • 5
  • 11
Red.Wave
  • 2,790
  • 11
  • 17
0

Here's the LLVM header.

// -*- C++ -*-
//===--------------------------- __nullptr --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP_NULLPTR
#define _LIBCPP_NULLPTR

#include <__config>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif

#ifdef _LIBCPP_HAS_NO_NULLPTR

_LIBCPP_BEGIN_NAMESPACE_STD

struct _LIBCPP_TEMPLATE_VIS nullptr_t
{
    void* __lx;

    struct __nat {int __for_bool_;};

    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t() : __lx(0) {}
    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t(int __nat::*) : __lx(0) {}

    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR operator int __nat::*() const {return 0;}

    template <class _Tp>
        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
        operator _Tp* () const {return 0;}

    template <class _Tp, class _Up>
        _LIBCPP_INLINE_VISIBILITY
        operator _Tp _Up::* () const {return 0;}

    friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator==(nullptr_t, nullptr_t) {return true;}
    friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator!=(nullptr_t, nullptr_t) {return false;}
};

inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t __get_nullptr_t() {return nullptr_t(0);}

#define nullptr _VSTD::__get_nullptr_t()

_LIBCPP_END_NAMESPACE_STD

#else  // _LIBCPP_HAS_NO_NULLPTR

namespace std
{
    typedef decltype(nullptr) nullptr_t;
}

#endif  // _LIBCPP_HAS_NO_NULLPTR

#endif  // _LIBCPP_NULLPTR

(a great deal can be uncovered with a quick grep -r /usr/include/*`)

One thing that jumps out is the operator * overload (returning 0 is a lot friendlier than segfaulting...). Another thing is it doesn't look compatible with storing an address at all. Which, compared to how it goes slinging void*'s and passing NULL results to normal pointers as sentinel values, would obviously reduce the "never forget, it might be a bomb" factor.

l.k
  • 199
  • 8
0

According to cppreference, nullptr is a keyword that:

denotes the pointer literal. It is a prvalue of type std::nullptr_t. There exist implicit conversions from nullptr to null pointer value of any pointer type and any pointer to member type. Similar conversions exist for any null pointer constant, which includes values of type std::nullptr_t as well as the macro NULL.

So nullptr is a value of a distinct type std::nullptr_t, not int. It implicitly converts to the null pointer value of any pointer type. This magic happens under the hood for you and you don't have to worry about its implementation. NULL, however, is a macro and it is an implementation-defined null pointer constant. It's often defined like this:

#define NULL 0

i.e. an integer.

This is a subtle but important difference, which can avoid ambiguity.

For example:

int i = NULL;     //OK
int i = nullptr;  //error
int* p = NULL;    //OK
int* p = nullptr; //OK

and when you have two function overloads like this:

void func(int x);   //1)
void func(int* x);  //2)

func(NULL) calls 1) because NULL is an integer. func(nullptr) calls 2) because nullptr converts implicitly to a pointer of type int*.

Also if you see a statement like this:

auto result = findRecord( /* arguments */ );

if (result == nullptr)
{
 ...
}

and you can't easily find out what findRecord returns, you can be sure that result must be a pointer type; nullptr makes this more readable.

In a deduced context, things work a little differently. If you have a template function like this:

template<typename T>
void func(T *ptr)
{
    ...
}

and you try to call it with nullptr:

func(nullptr);

you will get a compiler error because nullptr is of type nullptr_t. You would have to either explicitly cast nullptr to a specific pointer type or provide an overload/specialization for func with nullptr_t.


Advantages of using nulptr:
  • avoid ambiguity between function overloads
  • enables you to do template specialization
  • more secure, intuitive and expressive code, e.g. if (ptr == nullptr) instead of if (ptr == 0)
jignatius
  • 6,304
  • 2
  • 15
  • 30
-2

NULL need not to be 0. As long you use always NULL and never 0, NULL can be any value. Asuming you programme a von Neuman Microcontroller with flat memory, that has its interrupt vektors at 0. If NULL is 0 and something writes at a NULL Pointer the Microcontroller crashes. If NULL is lets say 1024 and at 1024 there is a reserved variable, the write won't crash it, and you can detect NULL Pointer assignments from inside the programme. This is Pointless on PCs, but for space probes, military or medical equipment it is important not to crash.

  • 2
    Well, the actual value of null pointer in memory may not be zero, but C (and C++) standard mandates compilers to convert integral 0 literal to null pointer. – bzim Sep 16 '18 at 05:44
  • Legend has it that in some Honeywell computers, NULL was not zero but 06000. See https://stackoverflow.com/questions/2597142/when-was-the-null-macro-not-0 – Pierre Aug 09 '21 at 12:01