What is the problem with NULL?
Traditionally, C & C++ programs are using NULL macro to denote null pointer. This macro is actually not a pointer but an integer constant value 0. However, there are fundamental problems in using this macro. The nullptr ultimately solves those problems in C++11.
Firstly, the underlying implementations may assign this to any of integral types. For instance, depending on the platform, the 0 value may either be an “int” or may be a “long”. Consequently, due to overloading on any of integral or pointer types may provide unexpected results.
The following example shows the a call to overloaded function “fun” shall give unexpected result. In particular, the first overload expects an integer type argument and second overload expects a pointer. However, when NULL value is passed, the compiler will get confused. This is because with NULL the user may want to call the pointer version. However, since, the implementation is done using integral type, therefore, integral version is also possible match.
#include <iostream> //main header
using namespace std; //namespace
void funda(int)
{
cout << "funda(int)" << endl;
}
void funda(int*)
{
cout << "funda(int*)" << endl;
}
int main()
{
funda(NULL);
return 0;
}
The error thrown by compiler is:
Understanding nullptr
In C++11, nullptr will solve this ambiguity. Therefore, when we make following changes, the compiler will correctly compile the code.
// fun(NULL);
fun(nullptr);
The output that will be generated with above command is:
fun(int*)
How nullptr is different than NULL
nullptr do not have either an integral type or even a pointer type. The actual type of nullptr is nullptr_t instead. This is just like a pointer which can point to all data types. Therefore, when program passes nullptr, the compiler identifies that this value can get converted only to a pointer. Finally, correct overload is called.
Address of an object of nullptr_t type
It is possible to define any variable of type nullptr_t. This definition will be just like a normal variable definition. Therefore, it is valid to take the address of such a variable using ampersand (&) operator.
nullptr_t np; //np is like nullptr
nullptr_t * ptr_np = &np;
cout << ptr_np << endl;
The above code may generate an output, like, shown below.
0x7ffc48955e50
Address of nullptr
It is not possible to take the address of nullptr. This is because internally, it is an R-value of type nullptr_t.
nullptr_t *ptr_np = &nullptr;
For the above code, the compiler may generate an error:
Assignment to any pointer
The constant nullptr can be assigned to any pointer or to pointer to member. For instance, it can be assigned to any of the following types.
int *p = nullptr;
char *q = nullptr;
float *r = nullptr;
void *s = nullptr;
However, this value cannot be assigned to non-pointer.
int t = nullptr; //This is wrong
Compiler error:
Using sizeof & typeinfo
Since, the nullptr is like another variable, we can use sizeof and typeid. The following code is valid and produces output shown below.
cout << sizeof(nullptr) << endl;
cout << typeid(nullptr).name() << endl;
Output:
8
Dn
Comparison Operation
It is valid to compare two objects of type nullptr_t. This is because, they are just like ordinary variables of type nullptr_t. Also, we can compare an object and nullptr. nullptr is just like a constant of this type.
nullptr_t n1, n2;
if(n1 == n2)
cout << "Equal" << endl;
else
cout << "Not-Equal" << endl;
if(n1 == nullptr)
cout << "Equal to nullptr" << endl;
else
cout << "Not-Equal to nullptr" << endl;
Output
Equal
Equal to nullptr
Declaration using decltype
The decltype keyword returns the actual data type of any object. And this is true for nullptr also. The following example shows that this decltype actually returns a type “nullptr_t”.
decltype(nullptr) z;
if(z == nullptr)
cout << "This is nullptr" << endl;
Output
This is nullptr