C++ / a working model

46 / 163   ·   C++11   ·   7 min

Express null pointers with nullptr

Keep this sentence

nullptr is a null-pointer literal with its own type; it is neither integer zero nor some universal pointer. It keeps null-pointer semantics in overload resolution and template argument passing, but it does not make dereferencing safe and cannot replace object lifetime management.

In this lesson
  1. Distinguish value, type, and representation first
  2. Why overloads and templates need it more
  3. A null check is not an ownership check
  4. Example
  5. Exercise

Distinguish value, type, and representation first

The nullptr introduced in C++11 is a prvalue of type std::nullptr_t; that type itself is neither a pointer type nor a pointer-to-member type. It can convert to the null value of any pointer type, including object pointers, function pointers, and member pointers. Include <cstddef> when you need to write the type name explicitly.

A null pointer means a special state that does not point to an object or function; it does not promise that every bit on the machine is zero. Initialize with int* p = nullptr; do not treat zeroing memory as a portable way to initialize a pointer. Test for null with comparison or a condition; do not rely on how an address is printed.

Why overloads and templates need it more

If both choose(int) and choose(int*) exist, the literal 0 prefers the integer overload, while nullptr selects the pointer overload. The concrete definition of traditional NULL is implementation-defined, so it is not a reliable marker of overload intent. In modern code, use nullptr for a null pointer and 0 for numeric zero.

When a template takes an argument by value and then relays it, integer zero is deduced as an integer; the ordinary integer variable inside the function is not a null pointer constant. After nullptr is deduced as std::nullptr_t, it can still convert to a null pointer. That is not special template magic; it is the conversion capability preserved by a distinct type.

A null check is not an ownership check

The example first asserts which overload is chosen, then relays nullptr through a template to a function that only accepts a pointer, and finally binds the pointer to a still-living local object. Every dereference happens while the object is known to be valid, so dangerous code is not needed to demonstrate mistakes.

When an interface may omit an object, a pointer and nullptr express optional borrowing clearly; when the interface must receive an object, a reference is often more direct. A non-null pointer can still dangle; if (p) only tests for null and does not prove the object is alive. Hand resource ownership to RAII or smart pointers; you cannot repair that with a null check before a call.

Pitfalls

  • nullptr cannot resolve ambiguity between two different pointer overloads; when both f(int*) and f(double*) exist, f(nullptr) may still fail to select one.
  • Keeping a dangling pointer as a non-null value does not restore validity through a null check; only a clear ownership and lifetime relationship prevents dangling.

Run an example

Minimum C++11 · complete program · Download .cpp

#include <cassert>
#include <cstddef>
#include <iostream>
#include <type_traits>

int choose(int) { return 1; }
int choose(int*) { return 2; }
bool is_empty(int* p) { return p == nullptr; }
template<class T>
bool relay(T value) { return is_empty(value); }

int main() {
    static_assert(std::is_same<decltype(nullptr), std::nullptr_t>::value, "null type");
    assert(choose(0) == 1);
    assert(choose(nullptr) == 2);
    assert(relay(nullptr));
    int n = 7;
    int* p = nullptr;
    assert(!p);
    p = &n;
    assert(*p == 7);
    std::cout << choose(nullptr) << ' ' << *p << '\n';
}

Compile locally

g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread modern-nullptr.cpp -o example && ./example

Expected result

2 7

CHECK YOUR UNDERSTANDING

Close the answer. Explain it.

After adding choose(double*) to choose, how do you still call the int* overload explicitly? Why can you not keep passing nullptr directly?

Show a reference answer

Call choose(static_cast<int*>(nullptr)), or first declare int* p = nullptr and then call choose(p). Both pointer overloads get equally ranked null-pointer conversions from nullptr, so there is no better candidate; an explicit target type supplies the missing selection criterion without changing the null value itself.

Check the sources

Drafts and official chapters change. The version mark is only the example’s minimum.

Back to the catalog