Document #: | P2048R0 |
Date: | 2020-01-12 |
Project: | Programming Language C++ Evolution Working Group Incubator (EWGI) |
Reply-to: |
Vittorio Romeo
<vittorio.romeo@outlook.com> |
This paper proposes removing the NULL
macro and prohibiting the use of the integer literal 0
as a null pointer literal from C++23, limiting the change to epoch 2023.
None.
(This paper assumes that the reader is familiar with the concept of epochs and its goals, which are explained in P18811. Sections 3, 7, and 8.3 of the epochs paper provide context required to understand this paper.)
There currently are multiple ways to represent a null pointer constant in C++: the integer literal 0
, the standard macro NULL
, and the keyword nullptr
. While the latter is preferable in every scenario, due to backward compatibility, both 0
and NULL
still exist. They are both suboptimal in terms of functionality, and also needlessly increase the complexity and breadth of the language, often resulting in confusion especially to newcomers.
This paper proposes to make nullptr
the only valid way to represent a null pointer constant in C++, leveraging the epochs mechanism discussed in P18812 to avoid breaking backward compatibility.
0
is a suboptimal way to represent a null pointer constant for multiple reasons. Firstly, it is an integer literal, which is most commonly used to represent the numerical value zero rather than an address. Its duality introduces cognitive overhead to source code readers.
Before
|
After
|
---|---|
Another problem lies in overload resolution. Overloading a function accepting int*
with one accepting int
might silently change the meaning of existing code:
Before
|
After
|
---|---|
Under epoch 2023
, surprising behavior and bugs would be prevented:
Before
|
After
|
---|---|
NULL
macroThe NULL
macro is defined in the C++20 standard as “an implementation-defined null pointer constant”3. This gives implementations the freedom to implement NULL
as any zero integral literal (e.g. 0
, 0L
) or as nullptr
. Similarly to the overload resolution situation described above, this definition can cause problem when new overloads are added to an existing overload set.
Imagine adding a function accepting either std::nullptr_t
, long
, int
, or int*
to an existing overload set that is being invoked with NULL
: the behavior is now hard to predict and may vary depending on the standard library implementation being used.
Modern code should use nullptr
instead of 0
or NULL
to maximize readability and prevent suprising overload resolution outcomes between pointer and integral types. epoch 2023
forbids the use of the integer literal 0
in a context where a pointer is required, and the use of NULL
altogether:
Before
|
After
|
---|---|
In layman’s terms, this is what the compiler would do in a module unit targeting epoch 2023
:
Any use of the standard NULL
macro would be ill-formed. Ideally, the error would explain that NULL
is a relic of the past and that nullptr
should be used instead.
Any use of the integer literal 0
(or similar, such as 0L
) in a context where a pointer type is required would be ill-formed.
0
will find overloads accepting integral types as suitable, but will never select any overload accepting a pointer type.Will be provided if the paper is positively received. Minor changes are required in [support.types.nullptr]
and [conv.ptr]
.
None.