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.
Related
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.
The bool data type is commonly represented as 0 (as false) and 1 (as true). However, some say that true values can be represented by a value other than 1. If the later statement is true, then the following expression may be incorrect.
bool x = 1;
if (x==1)
Do something..
I am wondering if the following statements would work as desired and expected on commonly used compilers.
bool x = 1;
if (x==1)
Do something.
bool y = 0;
if (y>0.5)
Do something..
bool z = 1;
if(z>0.5)
Do something...
§4.5 of the C++ standard says:
An rvalue of type bool can be
converted to an rvalue of type int,
with false becoming zero and true
becoming one.
regarding 2 and 3, type conversion takes place so the statements will work as desired
According to the rule of Boolean conversions:
A prvalue of integral, floating-point, unscoped enumeration, pointer,
and pointer-to-member types can be converted to a prvalue of type
bool.
The value zero (for integral, floating-point, and unscoped
enumeration) and the null pointer and the null pointer-to-member
values become false. All other values become true.
Then
bool x = 1; // x will be true
bool y = 0; // y will be false
bool z = 1; // z will be true
For the 1st case, if (x==1), x will be promoted to int,
the type bool can be converted to int with the value false becoming 0 and true becoming 1.
then (x==1) is true.
For the second case, if (y>0.5), y will be promoted to int with value 0, then converted to double for the comparison;
If the operands has arithmetic or enumeration type (scoped or unscoped), usual arithmetic conversions are performed on both operands following the rules for arithmetic operators. The values are compared after conversions:
and
If the operand passed to an arithmetic operator is integral or unscoped enumeration type, then before any other action (but after lvalue-to-rvalue conversion, if applicable), the operand undergoes integral promotion.
...
Otherwise, if either operand is double, the other operand is converted to double
then y>0.5 is false.
For the third case, if (z>0.5), z will be promoted to int with value 1, then converted to double for the comparison; then z>0.5 is true.
if (x==1) is not incorrect. All true value representations are converted to 1 when you convert a boolean to a numeric type.
Given bool z=true, if(z>0.5) will be true, because 1.0 is greater than 0.5.
bool has only two values, and they are true and false. 1 and 0 are integer literals and as such they can be converted to bool. You have to consider that the conversion works in both directions, but you do not necessarily get back the same integer:
int a = 5;
bool b = a; // int -> bool conversion
int c = b; // bool -> int conversion
std::cout << a << " " c;
prints:
5 1
Any integer value other than 0 gets converted to true, but true gets always converted to 1.
Keeping this in mind, all your examples will work as expected. However, note that bools main purpose is that we can use true and false in our code instead of having to give numbers as 0 and 1 special meaning. It is always better to be explicit, so when you mean true you better write true not 1.
A relevant question here is "why do we even allow comparison between a bool and an int?".
The answer is backward compatibility and compromise, because C and C++ used to recommend integers to store Boolean values. So there was a lot of code that
Should keep working in spite of new language rules, such that
relational operators return a bool instead of an int.
Allow you to upgrade old code by changing the declaration of a Boolean variable
with a minimum of follow-up changes in the rest of the code.
Other languages may be cleaner in this respect, but the bool type has apparantly been a success.
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.
If a C++ program applies the bitwise-not operator (~) to a boolean value, does that invoke Undefined Behavior?
E.g. is the following program well-defined?
bool f = false;
bool f2 = ~f; // is f2 guaranteed to be true, or is this UB?
bool t = true;
bool t2 = ~t; // is t2 guaranteed to be false, or is this UB?
(Yes, I know there is a ! operator that is better-suited to this sort of thing; for purposes of this question we will ignore its existence ;))
5.3.1/10 The operand of ~ shall have integral or unscoped enumeration type; the result is the one’s complement of its operand. Integral promotions are performed. [emphasis mine]
4.5/6 A prvalue of type bool can be converted to a prvalue of type int, with false becoming zero and true becoming one.
4.5/7 These conversions are called integral promotions.
So ~false is an int with a bit pattern consisting of all ones - one's complement of a bit pattern representing 0, namely all zeros (as required by 3.9.1/7.) Similarly, ~true is an int that's one's complement of the bit representation of 1 - namely, all ones with the least significant bit zero. Both these values will evaluate to true in boolean context.
The arithmetic operators perform the integral promotions on their operand. Specifically [expr.unary.op]/9 says that this happens for ~ too.
So ~t is the same as ~1. This gives a valid non-zero integer.
Integer-to-bool conversions are defined by [conv.bool]:
A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true
so bool t2 = ~t; yields t2 == true. There is no undefined behaviour.
~f is the same as ~0. In 2's complement, ~0 gives -1, so we will have f2 == true.
In 1's complement -- if there were ever a C++ system that uses 1's complement -- then the effect of ~0 is unclear.
bool t = true;
bool t2 = ~t; // is t2 guaranteed to be false, or is this UB?
I suppose it is not guaranteed, seeing how
bool b = true;
bool b1 = ~b;
cout << b1;
outputs a "true"
I suppose it has to do with boolean representation... if it is a byte, then 00000001 will negate to 11111110 which is not zero. Promotion might also be at play, but it's the same tune.
The key here is it is "bitwise" not "logical". So one should not expect the two to match, unless the boolean representation is a single bit.
Easily entirely defined behavior.
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.