74

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.

iFreilicht
  • 13,271
  • 9
  • 43
  • 74
  • @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. – iFreilicht Apr 16 '14 at 11:01
  • 1
    @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. – iFreilicht Apr 16 '14 at 11:02
  • Is there a missing word in the title? – A.L Apr 17 '14 at 20:37
  • 1
    @n.1 no? short-circuit is a verb in this case. – iFreilicht Apr 17 '14 at 21:39
  • Ok, thanks, I didn't know it was a verb. – A.L Apr 18 '14 at 14:21

6 Answers6

79

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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 3
    My fault, it was probably just wishful thinking. Do you know of a specific reason why C++ doesn't have a `&&=` operator? – iFreilicht Apr 16 '14 at 11:04
  • 1
    @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" :-) – paxdiablo Apr 16 '14 at 11:09
  • 4
    @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. – iFreilicht Apr 16 '14 at 11:24
  • 15
    @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 :-) – paxdiablo Apr 16 '14 at 11:29
  • 1
    @paxdiabla You certainly don't have to, thanks for your precise answer! – iFreilicht Apr 16 '14 at 11:31
  • 2
    @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]. – supercat Jun 04 '14 at 16:23
  • @supercat 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`)? – dwanderson Mar 09 '16 at 20:08
  • @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. – supercat Mar 09 '16 at 20:17
  • @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. – Adrian Maire May 10 '16 at 15:15
  • 1
    @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. – supercat May 10 '16 at 15:28
  • 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. – Adrian Maire May 10 '16 at 15:57
  • @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. – supercat May 10 '16 at 17:02
15

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.

Jorge Bellon
  • 2,901
  • 15
  • 25
quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
14

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

Wojtek Surowka
  • 20,535
  • 4
  • 44
  • 51
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.

  • 2
    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? – dwanderson Mar 09 '16 at 21:18
5

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++.

Danvil
  • 22,240
  • 19
  • 65
  • 88
0

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
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Dr. Debasish Jana
  • 6,980
  • 4
  • 30
  • 69