{{announcement.body}}
{{announcement.title}}

What Exactly Nullptr Is in C++?

DZone 's Guide to

What Exactly Nullptr Is in C++?

In this article, we discuss some basics behind nullptr in C++, including what it is, why it's useful, and when we can use it.

· Web Dev Zone ·
Free Resource

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

Why Do We Need Nullptr?

To distinguish between an integer, 0 (zero), i.e. NULL, and an 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 an integer literal that evaluates to zero.
  • For those of you who believe that NULL is the same i.e. (void*)0 in C and C++. I would like to clarify that no it's not:
  • C++ requires that macro NULL 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

Implicit Conversion

C++
 




xxxxxxxxxx
1


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



Function Calling Ambiguity

C
 




xxxxxxxxxx
1


 
1
void func(int) {}
2
void func(int*){}
3
void func(bool){}
4
 
          
5
func(NULL);     // Which one to call?



Compilation produces the following error:

Plain Text
 




xxxxxxxxxx
1


 
1
error: call to 'func' is ambiguous    func(NULL);    ^~~~
2
note: candidate function void func(bool){}                              ^
3
note: candidate function void func(int*){}                              ^
4
note: candidate function void func(int){}                              ^
5
1 error generated.
6
compiler exit status 1



Constructor Overload

C++
 




xxxxxxxxxx
1


 
1
struct String
2
{    String(uint32_t)    {   /* size of string */    }    String(const char*) {       /* string */        }
3
};
4
 
          
5
String s1( NULL );
6
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 the Return Type Resolver idiom that automatically deduces a null pointer of the correct type depending upon the type of the instance it is assigning to.

Consider the following simplest and most unsophisticated nullptr implementation:

C++
 




xxxxxxxxxx
1


 
1
struct nullptr_t {    void operator&() const = delete;  // Can't take address of nullptr
2
    template<class T>    inline operator T*() const { return 0; }
3
    template<class C, class T>    inline operator T C::*() const { return 0; }
4
};
5
nullptr_t nullptr;



If the above code seems strange and weird to you, then I would suggest you go through my earlier article on advanced C++ concepts. The magic here is just the templatized conversion operator.

If you are into a more authoritative source, then, here is a concrete implementation of nullptr from LLVM header.

Use-Cases of Nullptr

C++
 




xxxxxxxxxx
1


 
1
struct C { void func(); };
2
 
          
3
int main(void)
4
{    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.
5
}



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

Function Calling Clarity With Nullptr

C++
 




xxxxxxxxxx
1


 
1
void func(int)   { /* ... */}
2
void func(int *) { /* ... */}
3
void func(bool)  { /* ... */}
4
 
          
5
func(nullptr);



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

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:

C++
 




xxxxxxxxxx
1


 
1
// int ptr_not_ok = reinterpret_cast<int>(nullptr); // Not OK
2
long ptr_ok = reinterpret_cast<long long>(nullptr); // OK



A reinterpret_cast cannot convert nullptr_t to any pointer type. Use static_cast instead.

C++
 




xxxxxxxxxx
1


 
1
void func(int*)    { /*...*/ }
2
void func(double*) { /*...*/ }
3
 
          
4
func(nullptr);                            // compilation error, ambiguous call!
5
 
          
6
// func(reinterpret_cast<int*>(nullptr)); // error: invalid cast from type 'std::nullptr_t' to type 'int*'
7
func(static_cast<int*>(nullptr));         // OK



nullptr is implicitly convertible to any pointer type so explicit conversion with static_cast is only valid.

Nullptr_t Is Comparable

C++
 




xxxxxxxxxx
1


 
1
int *ptr = nullptr;
2
if (ptr == 0);          // OK
3
if (ptr <= nullptr);    // OK        
4
 
          
5
int a = 0;
6
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.

C++
 




xxxxxxxxxx
1


 
1
const int a = 0;
2
if (a == nullptr); // OK
3
 
          
4
const int b = 5;
5
if (b == nullptr); // error: invalid operands of types 'const int' and 'std::nullptr_t' to binary 'operator=='



Template-Argument Is of Type std::nullptr_t

C++
 




xxxxxxxxxx
1


 
1
template <typename T>
2
void ptr_func(T *t) {}
3
 
          
4
ptr_func(nullptr);         // Can not deduce T



As discussed earlier, Return Type Resolver needs an assignee to deduce the type.

C++
 




xxxxxxxxxx
1


1
template <typename T>
2
void val_func(T t) {}
3
 
          
4
val_func(nullptr);         // deduces T = nullptr_t
5
val_func((int*)nullptr);   // deduces T = int*, prefer static_cast though



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.

The conversion is only allowed for direct-initialization — not copy-intialization, which includes the case for passing an argument to a function by value. e.g.

C++
 




xxxxxxxxxx
1


 
1
bool b1 = nullptr; // Not OK
2
bool b2 {nullptr}; // OK
3
 
          
4
void func(bool){}
5
 
          
6
func(nullptr);     // Not OK, need to do func(static_cast<bool>(nullptr));



Misc

C++
 




xxxxxxxxxx
1


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



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 and literals, as they have a type ( bool ). nullptr is a pointer literal of type std::nullptr_t, and 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, and 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 use direct-initialization. i.e. bool is_false{nullptr};. Otherwise, you need to use static_cast.

How is nullptr defined?

It's just the templatized conversion operator known as Return Type Resolver.


References

You can find similar resources here, here, and in nullptr proposal(N2431); however, this post will walk you through the ins and outs of the spec step-by-step in a more friendly way so that you come away with a full understanding of the concept without any needless confusion

Topics:
c ,c++ 11 ,c++ programming ,null ,pointer ,tutorial ,web dev

Published at DZone with permission of Vishal Chovatiya . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}