Is nullptr falsy?
According to the C++ 17 Standard (5.13.7 Pointer literals)
1 The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is neither a pointer type nor a pointer-to-member type; rather, a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. See 7.11 and 7.12. — end note ]
And (7 Standard conversions)
4 Certain language constructs require that an expression be converted to a Boolean value. An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t (11.6).
And at last (7.14 Boolean conversions)
1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer-to-member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. For direct-initialization (11.6), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.
That is you may write for example
bool b( nullptr );
but you may not write (though some compilers have a bug relative to this)
bool b = nullptr;
So nullptr
can be contextually converted to an object of the type bool for example in selection statements like the if-statement.
Let's consider for example the unary operator !
as in an if statement
if ( !nullptr ) { /*...*/ }
According to the description of the operator (8.5.2.1 Unary operators)
9 The operand of the logical negation operator ! is contextually converted to bool (Clause 7); its value is true if the converted operand is false and false otherwise. The type of the result is bool
So nullptr
in this expression is not converted to a pointer. It is directly contextually converted to bool.
The result of your code is guaranteed, [dcl.init]/17.8
Otherwise, if the initialization is direct-initialization, the source type is
std::nullptr_t
, and the destination type isbool
, the initial value of the object being initialized isfalse
.
That means, for direct-initialization, a bool
object may be initialized from nullptr
, with the result value false
. Then for (bool)(nullptr)
, nullptr
is converted to bool
with value false
.
When using nullptr
as condition of if
or the operand of operator!
, it's considered as contextual conversions,
the implicit conversion is performed if the declaration
bool t(e);
is well-formed
That means, both if (nullptr)
and !nullptr
, nullptr
will be converted to bool
with value false
.
Yes, but you should avoid using this fact.
Comparing pointers to false
, or to 0
, is a common trope in C/C++ coding. I suggest that you avoid using it. If you want to check for nullness, use:
if (x == nullptr) { /* ... */}
rather than
if (!x) { /* ... */}
or
if (not x) { /* ... */}
The second variant adds another bit of confusion for the reader: What is x
? Is it a boolean? A plain value (e.g. an integer)? A pointer? An optional? Even if x
has a meaningful name, it won't help you much: if (!network_connection)
... it could still be a complex structure convertible to an integer or a boolean, it might be a boolean indicator of whether there's a connection, it could a pointer, a value or an optional. Or something else.
Also, remembering that nullptr
evaluates to false is another bit of information you need to store in the back of your brain to properly decode the code you're reading. We may be used to it from the olden days or from reading other people's code - but if we weren't, it would not have been obvious that nullptr
behaves like that. In a sense, it's not dissimilar for other obscure guarantees, like how the value at index 0 of an empty std::string
is guaranteed to be \0
. Just don't make your code rely on this stuff unless you absolutely have to.
PS : There is actually a lot less use for null pointers these days. You can force pointers to never be null if they don't need to; you can use references instead of pointers; and you can use std::optional<T>
to return either a T
or "no T". Perhaps you could just avoid mentioning nullptr
altogether.