views:

188

answers:

8

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.

A: 

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.

Kip
thoughts on += ?
caspin
If both variables are declared as '`bool`', under what circumstances (other than a broken compiler - or a non-standard, aka pre-standard compiler) will they go wrong?
Jonathan Leffler
if anything ever gets passed as a void* or something, and you have an API using the type as an int, you can't be guaranteed that they are always going to use "1" for true, as opposed to any other non-zero integer.
Kip
@Kip: you say that "if the variables are not `bool` then maybe all hell could break loose", which is a contradiction of my starting position of "if both variables are declared as `bool`". However, I see where you are coming from...if the variables are not strictly booleans, then the behaviour of the operators is unpredictable because the range of values is not predictable. Going back to my actual question - if both variables ARE in fact of type `bool`, is there any way in which the expressions will go wrong?
Jonathan Leffler
@kip: Are you saying that the operators shouldn't be used with bools because they won't work with non-bools? That seems a bit backwards.
jalf
In C++, this cannot happen because `bool` is an own fundamental type. It's not a typedef.
Johannes Schaub - litb
A: 
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.

Jonathan Leffler
A: 

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.

Tim Sylvester
+1  A: 

Could you just use the ternary operator?

old_value = !old_value ? possible_new_value : old_value;

Cinder6
why would that be preferrable to the shorter `old_value = old_value || new_value;` ?
Kip
Seems I read the question wrong :) There's no reason to do that instead of `old_value = old_value || new_value;`, especially because the latter is more readable.
Cinder6
A: 

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);
sth
Of course, this only works when x is a variable, and only fails obviously when x doesn't wind up with an lvalue, and depends crucially on constructing an expression half in and half out of the macro. Ewwwww.
David Thornley
This macro will fail if evaluation of `possible_new_value` has a side-effect.
Johannes Schaub - litb
If it fails depends on what the proper semantics of `||=` should be. I'm not really sure if short circuiting would be intended there or not.
sth
+7  A: 

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.

David Thornley
awesome, thanks. I'd up vote twice if I could
caspin
+1 for citing the standard (and answering the question), while also adding the comment about style
Gabe
+4  A: 

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.

Tyler McHenry
Nice inclusion of 5.17-7.
David Thornley
+1 for quoting the standard, and for the explicit breakdown of +=, and for the advice to avoid +=.
Mark Ransom
A: 

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.

Mark Ransom