Is nullptr falsy? - c++

When used as a boolean expression or transformed into a boolean either explicitly or implicitly, is nullptr consistently false? Is this implementation defined or specified in the standard?
I wrote some code to test, but am not certain if it tests this property fully. I couldn't find an existing SO answer that talked specifically about this. cppreference doesn't mention this from what I see.
if (nullptr) {
;
} else {
std::cout << "Evaluates to false implicitly\n";
}
if (!nullptr) {
std::cout << "Evaluates to false if operated on\n";
}
if (!(bool)(nullptr)) {
std::cout << "Evaluates to false if explicitly cast to bool\n";
}
Expected and actual:
Evaluates to false implicitly
Evaluates to false if operated on
Evaluates to false if explicitly cast to bool

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 is bool, the initial value of the object being initialized is false.
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.

Related

Does the 'if' statement in C/C++ cast the operand to an integer?

When you use the if statement in C/C++, or any other logical operator, is the operand you pass to the statement cast to an integer for evaluation?
My current understanding was that the operand passed to the statement is cast to an integer to test whether or not it is non-zero (true), and that if you pass a pointer, this can be cast to an integer to evaluate with a 0/null value being defined as false.
I was under the impression that C++'s standard Bool values were simply typedef of an unsigned char with a value of 0 and 1.
Can anyone explain what's actually happening behind the scenes with this behavior?
In C++ bool is a standalone type that has nothing to do with unsigned char. And in C++ language the expression under if is indeed implicitly cast to bool type. Note: to bool specifically, not to int. For scalar types other than bool, the implicit conversion to bool is essentially defined through non-equality comparison to literal 0. I.e. values that compare non-equal are converted to true, while values that compare equal - to false.
In other words, in C++ statement
if (a)
is interpreted as
bool tmp = (bool) a;
// `tmp` is either `true` or `false`
if (tmp)
which for scalar types is interpreted as
bool tmp = a != 0;
if (tmp)
Note that this works "as expected" for pointer types as well. But instead of converting the pointer to int type, it actually works the other way around: it converts literal 0 to the proper pointer type.
For other types it could be something different, like a call to a user-defined conversion operator operator bool().
In C language the expression under if has to have scalar type. And it is implicitly compared to constant 0. Note that this does not involve converting the controlling expression it to int. Comparison to constant 0 is defined separately for all scalar types. E.g. for int it has its natural meaning, while for pointer types it is interpreted as comparison to null pointer. Now, the result of such comparison in C has type int and evaluates to either 0 or 1. That 0 or 1 is what controls what if branch is taken.
In other words, in C statement
if (a)
is immediately interpreted as
int tmp = a != 0;
// `tmp` is either `0` or `1`
if (tmp)
Additionally, your assumption that null pointer produces a zero int value when converted to int type is incorrect. Neither language makes such guarantee. Null pointer is not guaranteed to be represented by zero address value and is not guaranteed to produce zero value when converted to int.
In C++, the condition in an if statement is converted to a bool.
From the C++11 Standard:
6.4 Selection statements
4 The value of a condition that is an initialized declaration in a statement other than a switch statement is the value of the declared variable contextually converted to bool (Clause 4). If that conversion is ill-formed, the program is ill-formed.
A different section, 4.12 Boolean conversion, of the standard talks about conversions to bool.
4.12/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. A prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.

using Boolean to represent integers

In c++, bool is used to represent Boolean. that is it holds true or false. But in some case we can use bool to represent integers also.
what is the meaning of bool a=5; in c++?
"what is the meaning of bool a=5; in c++?"
It's actually equivalent to writing
bool a = (5 != 0);
"But in some case we can use bool to represent integers also."
Not really. The bool value only represents whether the integer used to initialize it was zero (-> false) or not (-> true).
The other way round (as mentioned in #Daniel Frey's comment) false will be converted back to an integer 0, and true will become 1.
So the original integer value's (or any other expression results like pointers, besides nullptr, or double values not exactly representing 0.0) will be lost.
Conclusion
As mentioned in LRiO's answer, it's not possible to store information other than false or true in a bool variable.
There are guaranteed rules of conversion though (citation from cppreference.com):
The safe bool problem
Until the introduction of explicit conversion functions in C++11, designing a class that should be usable in boolean contexts (e.g. if(obj) { ... }) presented a problem: given a user-defined conversion function, such as T::operator bool() const;, the implicit conversion sequence allowed one additional standard conversion sequence after that function call, which means the resultant bool could be converted to int, allowing such code as obj << 1; or int i = obj;.
One early solution for this can be seen in std::basic_ios, which defines operator! and operator void* (until C++11), so that the code such as if(std::cin) {...} compiles because void* is convertible to bool, but int n = std::cout; does not compile because void* is not convertible to int. This still allows nonsense code such as delete std::cout; to compile, and many pre-C++11 third party libraries were designed with a more elaborate solution, known as the Safe Bool idiom.
No, we can't.
It's just that there's an implicit conversion from integer to boolean.
Zero becomes false; anything else becomes true.
In fact this declaration
bool a=5;
is equivalent to
bool a=true;
except that in the first declaration 5 as it is not equal to zero is implicitly converted to true.
From the C++ Standard (4.12 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 (8.5), a prvalue of type std::nullptr_t can be
converted to a prvalue of type bool; the resulting value is false.
One more funny example
bool a = new int;
Of course it does not mean that we may use bool to represent pointers. Simply if the allocation is successfull then the returned poimter is not equal to zero and implicitly converted to true according to the quote I showed.
Take into account that till now some compilers have a bug and compile this code successfully
bool a = nullptr;
Though according to the same quote a valid declaration will look like
bool a( nullptr );
And, basically, I would opine that bool b=5 is "just plain wrong."
Both C and C++ have a comparatively weak system of typing. Nevertheless, I'm of the opinion that this should have produced a compile error ... and, if not, certainly a rejected code review, because it is (IMHO) more likely to be an unintentional mistake, than to be intention.
You should examine the code to determine whether b is or is not "really" being treated as a bool value. Barring some "magic bit-twiddling purpose" (which C/C++ programs sometimes are called-upon to do ...), this is probably a typo or a bug.
Vlad's response (below) shows what the standard says will happen if you do this, but I suggest that, "since it does not make human-sense to do this, it's probably an error and should be treated accordingly by the team.
A "(typecast)?" Maybe. A data-type mismatch such as this? Nyet.

Check for null pointer in a truth-value context

Lets say I have a pointer
MyType *ptr;
When checking the validity of that pointer in a "truth-value context" by the old standards I would write something like this
if (ptr) { ... // 1
while (ptr) { ... // 2
The thing is that in such "truth value contexes" we expect for the implicit conversion of a pointer to a boolean value to take place, so we would be pretty much be comparing
if (NULL != ptr) { ...
while (NULL != ptr) { ...
Yet comparing against a macro for the integer 0 is deprecated and C++11 proposes comparing against nullptr.
When in a truth value context though like (1) or (2) above where we don't explicitly say
if (nullptr != ptr) { ...
while (nullptr != ptr) { ...
what is our pointer compared against ? It's conversion to a boolean ? Do we have to explicitly compare against nullptr ?
The condition (if it's an expression) of an if statement is contextually converted to bool:
[stmt.select]/4 about the condition in selection statements (if, switch):
The value of a condition that is an expression is the value of the
expression, contextually converted to bool for statements other than switch; if that conversion is ill-formed, the program is ill-formed.
Contextual conversion to bool is defined as follows in [conv]/3:
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t. 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.
Here's the description of a conversion to bool for fundamental types [conv.bool]/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. A prvalue of type std::nullptr_t can be converted to a prvalue of
type bool; the resulting value is false.
So when we test a pointer if(ptr), we compare ptr to the null pointer value of that type. What's a null pointer value? [conv.ptr]/1
A null pointer constant is an integral constant expression prvalue of integer type that evaluates to
zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the
result is the null pointer value of that type and is distinguishable from every other value of object pointer or
function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the
same type shall compare equal.
This also describes what happens when we compare if(ptr != nullptr): The nullptr is converted to the type of ptr (see [expr.rel]/2), and yields the null pointer value of that type. Hence, the comparison is equivalent to if(ptr).
Let's say you have:
int* ip = foo();
if ( nullptr == ip )
{
}
It's as if you are saying:
int* ip = foo();
if ( (int*)0 == ip )
{
}
At that point, you are comparing two pointers of the same type.
This is what I found at cppreference.com
Explanation
The keyword nullptr denotes the null pointer literal. It is an unspecified prvalue of type std::nullptr_t. There exist implicit conversions from nullptr to null pointer value of any pointer type and any pointer to member type. Similar conversions exist for any value of type std::nullptr_t as well as for the macro NULL, the null pointer constant.

Magic of casting value to bool

Today I realized that casting a value to a bool is a kind of magic:
int value = 0x100;
unsigned char uc = static_cast<unsigned char>(value);
bool b = static_cast<bool>(value);
Both sizeof(uc) and sizeof(b) return 1. I know that uc will contain 0x00, because only the LSB is copied. But b will be true, so my assumption is that, when casting to bool, the value gets evaluated instead of copied.
Is this assumption correct? And is this a standard C++ behavior?
There's nothing magical about it. The conversion from int to unsigned char is defined as value % 256 (for 8-bit chars), so that is what you get. It can be implemented as copying the LSB, but you should still think about it in the semantic sense, not the implementation.
On the same vein, conversion of int to bool is defined as value != 0, so again, that is what you get.
Integral (and boolean) conversions are covered by the C++11 standard in [conv.integral] and [conv.bool]. For the C-style cast, see [expr.cast] and [expr.static.cast].
It is a part of the standard:
4.12 Boolean conversions [conv.bool]
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. A prvalue of type
std::nullptr_t can be converted to a prvalue of type bool; the
resulting value is false.
Yes, when casting to bool, the value gets evaluated, not copying.
In fact, in your example, as long as value is not 0, b will be true.
Update: Quoting from C++ Primer 5th Edition Chapter 2.1.2:
When we assign one of the nonbool arithmetic types to a bool object, the
result is false if the value is 0 and true otherwise.
According to the rules for C-style casts, (bool)value is effectively a static_cast. The first rule for static_cast then kicks in, which computes the value of a temporary "declared and initialized ... as by new_type Temp(expression);", i.e. bool Temp(value);. That's well-defined: Temp is true iff value != 0. So yes, value is "evaluated" in a sense.
Casting to bool is a feature inherited from plain old C. Originally, C didn't have a bool type, and it was useful to use other types in if statements, like so:
int myBool = 1;
if(myBool) {
// C didn't have bools, so we had to use ints!
}
void* p = malloc(sizeof(int));
if(!p) {
// malloc failed to allocate memory!
}
When you're converting to bool, it acts just like you're putting the statement in an if.
Of course, C++ is backwards compatible with C, so it adopted the feature. C++ also added the ability to overload the conversion to bool on your classes. iostream does this to indicate when the stream is in an invalid state:
if(!cout) {
// Something went horribly wrong with the standard output stream!
}
ISO/IEC C++ Standard:
4.12 Boolean conversions [conv.bool] 1 A prvalue of arithmetic... type can be converted to a prvalue of type bool. A zero value... is converted to false; any other value is converted to true.
So, since a prvalue is a value, you might say that the value gets evaluated, albeit that's kind of pleonastic.

behavior of bool with non-boolean operators

What I really want is a ||= operator.
old_value = old_value || possible_new_value;
old_value ||= possible_new_value;
The second line is a compiler error (c++ doesn't have a ||= operator).
So what are my other options?
old_value += possible_new_value;
old_value |= possible_new_value;
While I'm on the subject how does bool behave with other non-boolean operators?
-
-=
&
&=
...
I can verify these empirically, but I'm most interested in what the standard says.
According to 4.7 (Integral conversions), paragraph 4, "If the destination type is bool, see 4.12. If the source type is bool, the value false is converted to zero and the value true is converted to one." In 4.12, "An rvalue of arithmetic, enumeration, pointer, or pointer to member type can be converted to an rvalue 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."
In a context where bool operands are not allowed but integral operands are, the bool will be converted to an integral type. When the integer result is stored in a bool variable, it will be converted to bool.
Therefore, you will be able to use + and * as boolean or and and, and you can use | and & also. You can't get away with mixing them, as (bool1 + bool2) & bool3 will yield false if all three variables are true. ((1 + 1) & 1 is 2 & 1, which is 0, or false.)
Remember that | and || don't work identically even here. | will evaluate both sides, and then evaluate the bitwise or. || will evaluate the first operand, then only if that was false will evaluate the second.
I'm not going to discuss the stylistic issues here, but if I did anything like that I'd be sure to comment it so people knew what I was doing and why.
The standard sayeth:
4.5-4 "Integral Promotions"
An rvalue of type bool can be
converted to an rvalue of type int,
with false becoming zero and true
becoming one.
5.17-7 "Assignment Operators"
The behavior of an expression of the
form E1 op= E2 is equivalent to E1 =
E1 op E2 except that E1 is evaluated
only once. In += and -=, E1 shall
either have arithmetic type or be a
pointer to a possibly cvqualified
completely defined object type. In all
other cases, E1 shall have arithmetic
type.
4.12-1 "Boolean Conversions"
An rvalue of arithmetic, enumeration,
pointer, or pointer to member type can
be converted to an rvalue 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.
So this means that
b1 += b2
Where b1 and b2 are boolean will be equivalent to
b1 = b1 + b2
And b1 and b2 will be promoted to 0/1 integers, and then converted back to boolean on the rule that anything other than 0 is true.
So the truth table is
true false
true true true
false true false
so += does in fact work as ||= according to the standard. However, this will probably be confusing other programmers, so I would avoid it still.
Could you just use the ternary operator?
old_value = !old_value ? possible_new_value : old_value;
Don't use |= and &= with bools. They may work most of the time, but it is still wrong. Usually the bool type is just a glorified int or char. In older code that I've worked with, BOOL is just typedef'd to int or char. In these cases, you can get the wrong answer if somehow the bits have been manipulated (For example, 1&2 is 0 (false)). And I'm not sure, but I think the result of bitwise operators is going to be an int, even for bools.
if (!old_value)
old_value = possible_new_value;
This is a direct equivalent of the original condition. It might generate simpler code since it won't always assign to old_value - but I wouldn't expect the performance difference to be easily measurable in a big program.
One difference is that the logical operators such as || guarantee the order of evaluation and provide short-circuiting, where the bit-wise and arithmetic operators do not.
I believe the compiler will treat the non-logical operators by converting the bools to numeric values (0, 1) applying the operator and converting back. These conversions are strictly defined by the standard, e.g.:
An rvalue of arithmetic, enumeration, pointer, or pointer to member type can be converted to an rvalue 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.
Nasty macro hackery:
#define UPDATE(x) x = x
UPDATE(old_value) || possible_new_value;
But I don't recommend this at all. Macros like this are a very bad idea for several reasons.
A more sane function, but without the short-circuiting:
bool set_or(bool &old, bool value) {
return (old = old || value);
}
...
bool v = false;
set_or(v, true);
I believe the standard explicitly defines true and false as 1 and 0, so you can safely use bitwise operators on bool values. Other types that might be implicitly treated as bools in another context should be explicitly converted for this to work reliably.
I've seen Microsoft's compiler generate an ugly warning each time you do this, because it thinks there's a danger in implicitly converting the int result back to bool.