What Exactly Is nullptr in C++by@IndianWestCoast
4,379 reads
4,379 reads

What Exactly Is nullptr in C++

by Vishal ChovatiyaApril 25th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Nullptr is more than just a keyword in C++ & to explain that, I have written this article. This article is more or less the same thing which you can find here, here & in nullptr proposal(N2431) But before jump-into it, we will see issues with NULL & then we'll dive into the unsophisticated implementation of nullptr. Nullptr cannot be defined as (void*)0 in the C++ standard library. Null pointer is a subtle example of Return Type Resolver idiom to automatically deduce a null pointer of the correct type depending upon the type it is assigning to.

Company Mentioned

Mention Thumbnail
featured image - What Exactly Is nullptr in C++
Vishal Chovatiya HackerNoon profile picture

The answer to "What exactly nullptr is in C++?" would be a piece of cake for experienced C++ eyes & for those who are aware of Modern C++ i.e. keyword. But nullptr is more than just a keyword in C++ & to explain that, I have written this article. But before jump-into it, we will see issues with NULL & then we'll dive into the unsophisticated implementation of  nullptr & some use-cases of nullptr.

/!\: Originally published @

Note: This article is more or less the same thing which you can find here, here & in nullptr proposal(N2431) but in a bit organized & simplified way.

Why do we need nullptr?

To distinguish between an integer 0(zero) i.e. NULL & actual null of type pointer.

nullptr vs NULL

  • NULL is 0(zero) i.e. integer constant zero with C-style typecast to void*, while nullptr is prvalue of type nullptr_t which is integer literal evaluates to zero.
  • For those of you who believe that NULL is same i.e. (void*)0 in C & C++. I would like to clarify that no it's not:

NULL - (C)

NULL - (C++)

  • C++ requires that macro NULL to be defined as an integral constant expression having the value of 0. So unlike in C, NULL cannot be defined as (void *)0 in the C++ standard library.

Issues with NULL

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

Implementation of unsophisticated 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.
  • Consider the following simplest & unsophisticated nullptr implementation:
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;

Use-cases of 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.
  • As shown in the above example, 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.

1️⃣ Function calling clarity with nullptr

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

  • Now, func( int* ) will be called as nullptr will implicitly be deduced to int*.

2️⃣ Typecasting on nullptr_t

  • 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.
  • Casting nullptr_t to an integral type holds true as long as destination type is large enough. Consider this:
// int ptr_not_ok = reinterpret_cast<int>(nullptr); // Not OK
long ptr_ok = reinterpret_cast<long long>(nullptr); // OK
  • A reinterpret_cast cannot convert nullptr_t to any pointer type. Use static_cast instead.
void func(int*)    { /*...*/ }
void func(double*) { /*...*/ }

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

// func(reinterpret_cast<int*>(nullptr)); // error: invalid cast from type 'std::nullptr_t' to type 'int*'
func(static_cast<int*>(nullptr));         // OK
  • nullptr is implicitly convertible to any pointer type so explicit conversion with static_cast is only valid.

3️⃣ nullptr_t is comparable

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=='

From Wikipedia: - …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.

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️⃣ Template-argument is of type std::nullptr_t

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

ptr_func(nullptr);         // Can not deduce T
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️⃣ Conversion to bool from nullptr_t

From cppreference :
- In the context of a direct-initialization, a bool object may be initialized from a prvalue of type std::nullptr_t, including nullptr. The resulting value is false. However, this is not considered to be an implicit conversion.

bool b1 = nullptr; // Not OK
bool b2 {nullptr}; // OK

void func(bool){}

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

6️⃣ Misc

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

Summary by FAQs

When was nullptr introduced?

  • C++11

Is nullptr a keyword or an instance of a type std::nullptr_t?

  • Both true and false are keywords & literals, as they have a type ( bool ). nullptr is a pointer literal of type std::nullptr_t, & it's a prvalue (i.e. pure rvalue, you cannot take the address of it using &). For more.

What are the advantages of using nullptr?

  • No function calling ambiguity between overload sets.
  • You can do template specialization with nullptr_t.
  • Code will become more safe, intuitive & expressive. if (ptr == nullptr); rather than if (ptr == 0);.

Is NULL in C++ equal to nullptr from C++11?

  • Not at all. The following line does not even compile:
  • cout<<is_same_v<nullptr, NULL><<endl;

Can I convert nullptr to bool?

  • Yes. But only if you direct-initialization. i.e. bool is_false{nullptr};. Else need to use static_cast.

How is nullptr defined?

Have any suggestions, query or wants to say `Hi`?

Take the Pressure Off, you are just a click away. 🖱️

Previosuly published at