C++ used to lack a special value to indicate a null pointer. It used 0 for this purpose, something that was inherited from C. This led to some problems, which we'll show in this article. The new standard introduces a new reserved word nullptr, to designate a constant rvalue that represents a null pointer.
NULL is ZERO
A null pointer constant is an integral constant expression (expr.const) rvalue of integer type that evaluates to zero.
This was inherited from C where
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
Since there is no keyword in the language for a null pointer, both C and C++ compiler provide an implementation-dependent macro called NULL. In VC++ and GCC this is defined as
:
#ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif
Integer 0 implicitly converts to any pointer type. The use of 0 as a special value for null pointers can lead to some issues, the most important being with calling overloaded functions. Let's look at these two overloaded functions:
void foo(int) {cout << "int" << endl;} void foo(char*) {cout << "pointer" << endl;}
The q uestion is, which of the two would be called for foo(0)? The answer is the first one. To be able to call the second one with a null pointer an explicit cast to char* should be made, i.e. foo((char*)0). On the other hand, the following line of code is valid and calls that constructor of std::basic_string that takes a char*:
string str(false);
The nullptr Reserved Word
The new standard introduces a reserved word called nullptr to represent a null pointer. It designates a constant rvalue of type decltype(nullptr):
typedef decltype(nullptr) nullptr_t;
The nullptr_t type:
- Is a typedef for decltype(nullptr)
- Is a POD type convertible to a pointer type and pointer-to-member type
- Objects of this type can be copied and thrown
- It is possible to take the address of a nullptr_t object
- reinterpret_cast to/from a nullptr_t object is possible
- nullptr_t matches a T* and a T::* partial specialization
The nullptr:
- Is a literal (just like 0 or false)
- The address of nullptr cannot be taken
- Can be used with sizeof, typeid and ternary conditional operator
It is important to note that the standard library macro NULL is not (re-)defined to nullptr; the result of that would be a lot of broken code.
It is possible to use 0 (NULL) and nullptr interchangeably (which is important for compatibility), like in the following examples:
int* p1 = 0; int* p2 = nullptr; if(p1 == 0) {} if(p2 == 0) {} if(p1 == nullptr) {} if(p2 == nullptr) {} if(p1 == p2) {} if(p2) {}
It is not possible however to assign nullptr to an integer value, nor to compare nullptr to an integer.
int n1 = 0; // ok int n2 = nullptr; // error if(n1 == nullptr) {} // error if(n2 == nullptr) {} // error if(nullprt) {} // error
Also, because nullptr is an rvalue constant, it's not possible to assign it a value.
nullptr = 0;
For the overloading problem presented in the previous paragraph, use of nullptr would call the overload that takes a char*.
void foo(int) {cout << "int" << endl;} void foo(char*) {cout << "pointer" << endl;} foo(0); // calls foo(int) foo(nullptr); // calls foo(char*)
nullptr can be used with the ternary conditional operator:
int* p1 = ...; int* p2 = true ? p1 : nullptr;
However, the following cases are erroneous:
int* p2 = true ? 0 : nullptr; // incompatible types int n1 = true ? nullptr : nullptr; // nullptr cannot be converted to int int n2 = true ? 0 : nullptr; // incompatible types
Given these two template functions:
template<typename T> void fooptr(T* t); template<typename T> void foo(T t);
The following cases are possible:
fooptr((int*)0); // type T deduced to int fooptr((int*)nullptr); // type T deduced to int foo(0); // type T deduced to int foo((int*)0); // type T deduced to int* foo(nullptr); // type T deduced to nullptr_t foo((int*)nullptr); // type T deduced to int*
while these are errors:
fooptr(0); // error, type T cannot be deduced fooptr(nullptr); // error, type T cannot be deduced
Conclusions
The new C++ standard defines a reserved word nullptr (representing a constant rvalue of typenullptr_t) that designates a null pointer. For compatibility reasons 0 can still be used as null pointer, and 0 and nullptr can be used interchangeably; also for compatibility reasons, macro NULL will not be redefined to nullptr.