C++: nullptr

In C and C++, the preprocessor definition NULL is/was used to explicitly indicate that a pointer is not pointing anywhere right now.

The problem with NULL is that underneath it is just a plain 0, something like this:

#define NULL 0

The following code excerpt shows how this can turn into a problem:

#include <iostream>
 
void m(int x)
{
    std::cout << x << endl;
}
 
void m(const char* x)
{
    std::cout << (x == NULL ? "NULL" : x) << std::endl;
}
 
int main()
{
  m(12);
  m(NULL);
  return 0;
}

When that code is compiled, the following error is displayed:

test.cpp: In function 'int main()':
test.cpp:19:9: error: call of overloaded 'm(NULL)' is ambiguous
test.cpp:19:9: note: candidates are:
test.cpp:5:6: note: void m(int)
test.cpp:10:6: note: void m(const char*)

Why does the compiler consider the m(NULL) ambiguous?

Because it cannot decide if NULL is actually a pointer (because it is a 0) or an integer (because NULL is… a 0).

C++11 introduces the literal nullptr of type std::nullptr_t; a literal that unambiguously represents a pointer pointing nowhere, eliminating the concept of being a pointer pointing to memory address 0.

It has these advantages:

  • It is a strongly typed null pointer, like Java or C# null.
  • No ambiguity when trying to pass a null pointer to something accepting, say, an int.
  • It is a literal and a keyword, so it cannot be defined or used as a symbolic name.
  • It cannot be casted automatically to a numeric type, as NULL can.

So, if the example above is replaced with this construction, there is this code:

int main()
{
  m(12);
  m(nullptr);
  return 0;
}

The code will compile and execute with no problems.

Since the type of nullptr is std::nullptr_t, which is a native type, programmers can also write a specific overload when the nullptr literal is passed as a parameter, as in the following example:

void m(int x)
{
    std::cout << x << std::endl;
}
 
void m(const char* x)
{
    std::cout << x << std::endl;
}
 
void m(nullptr_t)
{
    std::cout << "nullptr!" << std::endl;
}
 
int main()
{
  m(12);
  m(nullptr);
  return 0;
}

5 thoughts on “C++: nullptr

  1. The type of nullptr is nullptr_t, and you can overload on it. This is most useful when you have a function f that is overloaded on two or more pointer types so that f(nullptr) is ambiguous. If you also overload f on nullptr_t, then f(nullptr) will always call f(nullptr_t).

  2. It’s been a long time, but I feel obliged to point out a minor mistake: in both 2003 and 2011 Standard versions NULL is implementation-defined, but a footnote explicitly forbids “(void*)0” definition.
    That’s not surprising, should NULL be defined as (void*)0, code like
    Foo* foo = NULL;
    would be ill-formed, since void* in C++ (in contrast to C) is not implicitly convertible to other pointer types. Same footnote mentiones “0” and “0L” as viable options.
    (Note that the example program could be well-formed if NULL was defined as 0)
    Hope it doesn’t sound negative – I consider your articles a nice introduction to C++11 features :)

      1. Cloooose, but not perfect :) If NULL was a plain zero, f(int) overload would be choosen, since it’s an exact match, while f(char*) requires pointer conversion, which is strictly worse match wrt overload resolution rules. You can easily check it by calling f(0). As of gcc 4.7.1, NULL seems to be defined as “__null”, which looks like a special internal symbol (especially since “NULL” appears unchanged in the diagnostics). Haven’t checked, but I bet it’s the same in many if not all previous releases.
        I promise to refrain from further harrasment :D

Leave a reply to ebasconp Cancel reply