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 mac
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
.
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.
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 usedNULL
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)
.
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 typestd::nullptr_t
.
It's useful because it does not implicitly convert to an integral value.
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
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
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.nullptr
is an integer literal with value zero, you can not able to use its address which we accomplished by deleting & operator.nullptr
in the first place?NULL
has some issue with it as below:char *str = NULL; // Implicit conversion from void * to char *
int i = NULL; // OK, but `i` is not pointer type
void func(int) {}
void func(int*){}
void func(bool){}
func(NULL); // Which one to call?
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
struct String
{
String(uint32_t) { /* size of string */ }
String(const char*) { /* string */ }
};
String s1( NULL );
String s2( 5 );
String s((char*)0))
.