Basic short-circuit evaluation question

Hi all,

So I have an if clause with short-circuit operator &&. I understand that if I have something like:

if (bool1 && bool2) { do.stuff(); },

and bool1 evaluates to false, then bool2 is never evaluated and the statement returns false (so do.stuff() is never executed).

But, if I have something like this:

if (bool1 && Foo.execute() { do.stuff(); },

where Foo.execute() attempts to execute some code, and returns a bool, would I be correct in assuming that, if bool1 is false, Foo.execute() is never called?

Thanks,

That is correct. If the statement can already be determined by the first operand, the rest of the condition is not evaluated.

See https://en.cppreference.com/w/cpp/language/operator_logical

  • Builtin operators && and || perform short-circuit evaluation (do not evaluate the second operand if the result is known after evaluating the first), but overloaded operators behave like regular function calls and always evaluate both operands
2 Likes

If you want the 2nd condition to always evaluate even if first one is false, just use & not &&

2 Likes

AFAIK, the single ‘&’ is a bitwise operator, it is for operating on bits (on integer values) and, while it may show the behavior you describe, it REALLY feels like that’s more a side effect than an intended feature. And if that’s the case, I believe relying on that should be avoided.

Or is there some special case for bools that I’m not aware of?

1 Like

A definite code smell there. Another programmer might come along and assume it was a typo and “fix” it.

Much clearer would be:

auto conditionA = someInt == 3;
auto conditionB = mustEvaluateThis();
if(conditionA && conditionB) {
    // do something
}
3 Likes

@yfede when you are using & you are indeed using bitwise operators to operate with 2 booleans rather than using “logic operations/truth tables”. The booleans are converted to integers 0 (if false) and 1 (if true) and applies a bitwise operation:

  • X & 1 = 1
  • X & 0 = 0
    (No matter the value of X, but in our case X will be only 0 or 1 as it’s just a boolean)

On the other hand the always used && (and) as I understsand doesn’t need to evaluate the 2nd term if the first one is false as it’s making a logic operation, while in the & case as you are doing “normal operations” it evaluates every term.

The thing is that if you want to always evaluate one condition you can just put it first, like:

if (mustEvaluate && conditionB) ...

But if for some very specific reason you must evaluate both conditions (i.e both are functions that not only return a boolean but also do other needed logic) then you can use &. I get many will dislike using it and prefer doing all this stuff in another way as it’s less readable, but it’s just an option.

But I bet someone else can shed some light into the why && vs & work different

TLDR: && = and, & = bitand

Is this the case or are you assuming that?

What if a compiler implements a bool version that behaves correctly like &&?

I wouldn’t be surprised, if that depends on the platform and therefore I wouldn’t tolerate that in my code.

It’s standard. “Arithmetic operators do not accept types smaller than int as arguments”, if no operand is floating-point then “both operands are integers”, and “if the source type is bool, the value false is converted to zero and the value true is converted to the value one of the destination type”. Personally, I don’t find precomputing each part any clearer than commenting “& intended”. Shortcircuit evaluation is not syntactically self-evident, and precomputing makes the difference seem greater than it is.

3 Likes

This “problem” only arises when you’re calling at least two functions which have side effects, where those side-effects are essential to the correct running of the program, and where you also need the return values from both those calls.

Now… if both those functions modify the program’s state, then you also need to worry about the order in which they get called. If you use & then the compiler could (and will) call them in an arbitrary order. That normally manifests when you compile your app with gcc you get weird behaviour that takes ages to track down.

Whenever you call any non-pure function which modifies state, a good rule of thumb is that it should get a statement to itself.

So IMHO the trick with & is bad in quite a few ways, not least because having to add a comment to stop people mis-understanding a deliberately weird syntax choice is pretty nasty.

Just making the calls in the order you need them to happen, and assigning the results to bool variables with useful names, and then checking those variables later in the if statement is almost always the best style to use.

4 Likes

Well, it may happen that the order doesn’t matter. Anyway, I’ve used this kind of thing temporarily, but always end up redesigning so that it’s not needed anymore. If both side effects need to be performed unconditionally, but both returns condition something else, most often the side effects and the returns are poorly conflated, and there’s something not quite logical about the structure.

1 Like

What about

bool anyChange = false;
for (auto& x : stuff)
    anyChange |= x.update();
if (anyChange)
    recomputeThings();

The order is preserved, even though it probably doesn’t matter.

2 Likes

Not a big fan of that, though sometimes if there’s a loop then it’s the simplest pattern.

But you should at least avoid the bitwise operator, e.g.

bool anyChange = false;

for (auto& x : stuff)
    anyChange = x.update() || anyChange;

if (anyChange)
    recomputeThings();

Though TBH I think I’d tend to write it like this for clarity:

bool anyChange = false;

for (auto& x : stuff)
    if (x.update())
        anyChange = true;

if (anyChange)
    recomputeThings();
4 Likes