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?
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
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?
@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
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.
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.
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.
obviously this is a simple example and we can be certain that getNumSamples() and getNumChannels() are just getters that donāt alter program state. But is doing this sort of thing considered bad practice? If those were functions that updated program state, then would it be a super no-no?
Would the preferred version be
auto numSamples = buffer.getNumSamples();
auto numChannels = buffer.getNumChannels();
maybeThisFunctionUpdatesBufferSize(numSamples, numChannels);
They are equivalent in practice, because also in the one-line version, all the expressions that appear as arguments of the function maybeThisFunctionUpdatesBufferSize are guaranteed to be evaluated (but not in a specific order! I also vaguely remember that the order in which they are evaluated is guaranteed, (from last argument to first?), but for code clarity I tend not to depend on that, and anyway that has nothing to do with short-circuit evaluation).
Short-circuit only comes into play when you have boolean operators, like &&, ||.
Thatās not true. The order of argument evaluation is unspecified, so code that depends on evaluation happening in a specific order is not portable.
In a function call, value computations and side effects of the initialization of every parameter are indeterminately sequenced with respect to value computations and side effects of any other parameter.
Ah thanks, good to know, I seem to have formed that impression just by watching the order they were called in various occasions while debugging. Iām editing my post above to avoid confusion to future readers
FWIW in my experience, GCC and Clang tend to evaluate in the opposite order, which is actually quite handy for catching mistakes.
Weāve had a couple of head-scratchers in soul where some code that worked perfectly well on our local machines (Clang) suddenly started failing CI in the linux (GCC) builds, because weād got some functions with side-effects in the arguments. In a big, complex codebase (like the soul compiler!), it can end up being incredibly hard to track down exactly where the mistake is, because the function call itself doesnāt look wrong, and the side-effects could be buried deeply.
Itād be nice if the compiler could detect and warn when you have side-effects in multiple argument expressions. In practice thereād probably be a lot of false alarms, but I think thatād make a great option for projects with stricter warning levels.