Do the &= and |= operators for bool short-circuit?
Asked Answered
E

6

82

When writing code like this in C++:

bool allTrue = true;
allTrue = allTrue && check_foo();
allTrue = allTrue && check_bar();

check_bar() will not be evaluated if check_foo() returned false. This is called short-circuiting or short-circuit evaluation and is part of the lazy evaluation principle.

Does this work with the compound assignment operator &=?

bool allTrue = true;
allTrue &= check_foo();
allTrue &= check_bar(); //what now?

For logical OR replace all & with | and true with false.

Exotoxin answered 16/4, 2014 at 10:43 Comment(5)
@BenjaminLindley ok, I changed my formulation a bit. It seems that lazy evaluation just means to skip evaluation of parts of the program if you don't need them, as in regular if-else-clauses. Short-circuiting is taking it a step further, but seems to follow the same basic thought.Exotoxin
@Mehrdad after reading the answers, I agree with you. There seems to be absolutely no reason whatsoever for C++ to not have a &&= assignment operator.Exotoxin
Is there a missing word in the title?Creon
@n.1 no? short-circuit is a verb in this case.Exotoxin
Ok, thanks, I didn't know it was a verb.Creon
T
85

From C++11 5.17 Assignment and compound 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.

However, you're mixing up logical AND which does short-circuit, and the bitwise AND which never does.

The text snippet &&=, which would be how you would do what you're asking about, is nowhere to be found in the standard. The reason for that is that it doesn't actually exist: there is no logical-and-assignment operator.

Transmute answered 16/4, 2014 at 10:52 Comment(12)
My fault, it was probably just wishful thinking. Do you know of a specific reason why C++ doesn't have a &&= operator?Exotoxin
@iFreilicht: any specific reason? Probably because C never had it and the original C++ compiler was more or less just a front end for C, meant to be "C with classes", not "C with classes and a couple of extra operators" :-)Transmute
@paxdiabla soooo why didn't C have it? I mean it surely seems like a useful thing to me and it wouldn't be too hard to implement either.Exotoxin
@iFreilicht, C didn't have it because Dennis Ritchie obviously didn't feel it was useful (we could ask him directly if he hadn't shuffled off this mortal coil, leaving many of us the lesser for it). ANSI C didn't have it because their mandate was to codify existing practice rather than create a new language. ISO didn't have it because they basically rubber-stamped ANSI (not in a bad way). It's not been added to C since then because it either hasn't been put forward as a proposal or it has and it didn't garner enough votes. I don't think I can provide more detail than that :-)Transmute
@paxdiabla You certainly don't have to, thanks for your precise answer!Exotoxin
@iFreilicht: There aren't a whole lot of cases where x &&= y; would be useful. If x and y are both known to be zero or one, then if (x) x=y; would be more efficient [actually, I wish x && y had been defined to yield y if x was non-zero, but it's decades too late for that].Archetype
@Archetype I came with a question and I think that's the answer - I was trying x = (y / z) || 1; hoping that it would give me y / z if y >= z, but in the case that it isn't, I don't want 0, I want 1. And you're saying something like that isn't doable (extrapolating from your example about x && y)?Rock
@dwanderson: Yeah. I think it's really a shame that the operators were defined as they are; I'm hard-pressed to think of cases where C's behavior for && and || is more useful than would be x?y:0 or x:y:x [but only evaluating x once] but there are many cases where the alternatives would be more useful, and of course a huge number of cases where either would have the same practical effect.Archetype
@supercat: if(x) x=y is definitively not more efficient than a supposed x &&=y: If jump and so invalidate all the pipeline of the processor, which could easily slow-down the operation by several cycles. also, your statement is 8 letters, while the other is 5 letters. Personally I use some kind of x = x&&y very often, and when x has a long name, this construction make a significant readability difference.Sleazy
@AdrianMaire: On many processors, evaluation of "x&&=y;" would require something like "load 0; test x; branch if zero to Q; test y; branch if zero to Q; load 1; Q: store x" while "if (x) x=y;" would require "test x; branch if zero to Q; load y; store x; Q:", and "x ?y:0" would require "load zero; test x; branch if zero to Q; load y; Q: store X". Only on processors with an instruction to conditionally load a register with 0 or 1 does "&&=" behave respectably.Archetype
You are right, although, clang make the same result as using if(x) x=y. IMO, a better solution could be achieved without jumping as soon as both booleans have a normalized representation.Sleazy
@AdrianMaire: If the Booleans are known to have a normalized representation, then more efficient code is possible. My point is that I think || and && would have been more helpful if they require that operand types be compatible but did not normalize operands to integer 0 and integer 1. Having to use !!x in the rare cases where normalization is required would have been a small burden compared to all the cases where normalization would be more expensive or semantically less helpful than passing through values.Archetype
P
17

No, they do not cut-short.

Note that the &= and |= operators are formed as &+= and |+=. Bit operators & and | do not perform shortcut evaluation.

Only boolean operators && and || perform it.

This means that a short-cutting operator would have to be traditionally named &&= and ||=. Some languages provide them. C/C++ does not.

Pluri answered 16/4, 2014 at 10:54 Comment(0)
S
14

The short-circuit (i.e. lazy) evaluation is only for logical && and ||. Bitwise & and | evaluate both arguments.

Scarfskin answered 16/4, 2014 at 10:51 Comment(0)
O
8

First: a &= b; is not the same as a = a && b;. a &= b; means a = a & b;. In C/C++ there is no a &&= b;.

Logical AND a && b is bit like a test for 1 bit. If the first "bit" is already 0, than the result will always be 0 no matter the second. So it is not necessary to evaluate b if the result is already clear from a. The C/C++ standard allows this optimization.

Bitwise AND a & b performs this test for all bits of a and b. So b needs to be evaluated if at least one bit in a would be non-zero. You could perhaps wish that if a==0, than b would not be evaluated, but this optimization is not allowed in C/C++.

Overlap answered 16/4, 2014 at 10:57 Comment(0)
H
5

The code allTrue &= check_foo(); is equivalent to allTrue = allTrue & check_foo() In which you are using bitwise AND and no lazy evaluation is performed.

The bitwise AND must take two arguments who's binary representation has the same length, and useslogical AND operation to compare each corresponding pair of bits.

Hasid answered 16/4, 2014 at 10:53 Comment(1)
Pretty sure you can use & and | with integer promotion, ie. uint16_t x = 0xabcd; uint8_t y = 0xef; uint16_t z = x | y; // z = 0xabef, so saying it's required that "binary representation has the same length" isn't true?Rock
G
2

Since & is a bit operation, check_foo()` will be evaluated irrespective of value of result

result = false;
result &= check_foo(); // check_foo() is needless called

However, check_foo() will not be called if you use && and result is false as in:

result = false;
result = result && check_foo(); // check_foo() is not called, the bitwise operator shortcircuits
Glyoxaline answered 16/4, 2014 at 10:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.