nullptr vs NULL in Modern C++
In modern C++, both nullptr and NULL
represent a null pointer value, but they operate under fundamentally
different rules. While NULL is a legacy macro inherited
from C that evaluates to an integer constant, nullptr is a
strongly typed keyword introduced in C++11 specifically to address type
ambiguity. Understanding the distinction between the two is essential
for writing safe, modern, and predictable C++ code.
The Underlying Definition
NULL is a preprocessor macro defined in headers like
<cstddef> or <cstdlib>. In C++,
NULL is defined as an integer zero (0 or
0L) because C++ does not allow the implicit conversion of
void* to other pointer types without a cast:
#define NULL 0Because it is an integral literal, NULL does not possess
a distinct pointer type.
In contrast, nullptr is a core language keyword of type
std::nullptr_t. It is not a macro and cannot be redefined.
While nullptr can implicitly convert to any raw pointer
type or member pointer type, it cannot implicitly convert to integral
types (except bool in conditional evaluations).
Function Overloading Ambiguity
The primary reason nullptr was introduced is function
overloading. Because NULL is effectively the integer
0, passing it to overloaded functions leads to unexpected
behavior or compilation errors:
void process(int value);
void process(char* ptr);
process(NULL); // Calls process(int), not process(char*)
process(nullptr); // Correctly calls process(char*)With process(NULL), the compiler resolves the call to
the integer overload because NULL is an integral type. If
an overload set contains multiple integer types or ambiguous pointer
signatures, NULL can cause a compilation error. Using
nullptr eliminates this ambiguity because it binds directly
to the pointer overload.
Template Deductions
The integral nature of NULL also causes failures in
generic programming. When passing arguments through templates, the
compiler deduces the type of the argument based on its actual type:
template <typename F, typename Arg>
void forwarder(F func, Arg arg) {
func(arg);
}
void targetFunction(int* ptr);
forwarder(targetFunction, NULL); // Compilation Error
forwarder(targetFunction, nullptr); // SucceedsIn this scenario, Arg is deduced as int
when using NULL. The template then attempts to call
targetFunction(int), which fails to compile because an
int cannot be passed to a function expecting an
int*. When using nullptr, Arg is
deduced as std::nullptr_t, which cleanly converts to
int*.
Summary of Differences
- Origin:
NULLis an inherited C preprocessor macro;nullptris a C++11 language keyword. - Type:
NULLis an integer literal (intorlong);nullptrhas its own type (std::nullptr_t). - Implicit Conversions:
NULLimplicitly converts to numeric types and pointers;nullptronly converts to pointer types (and contextually tobool). - Safety:
NULLrisks incorrect overload selection and template deduction failures;nullptrguarantees clear, type-safe pointer semantics.
In modern C++ development, NULL is considered obsolete.
Developers should exclusively use nullptr for all
pointer-nullifying operations.