if (cheapCheck() && expensiveCheck()) { ... }
is not equivalent to bool cheap = cheapCheck();
bool expensive = expensiveCheck();
if (cheap && expensive) { ... }
unless we're in a lazy pure functional language.The ability of the compiler to skip the expensive function in both cases are characteristics of a "lazy" language, ie. you can assign an expensive function to a value, but the actual function isn't run until the variable is needed. Languages like C++ can't do this because it would alter the program's behavior if cheapCheck() does return true, because suddenly it would have to call expensiveCheck() after cheapCheck() (rather than before), which is an observably different behavior for the optimization to cause.
> 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
[1] https://en.cppreference.com/w/cpp/language/operator_logical [2] https://en.wikipedia.org/wiki/Short-circuit_evaluation
If the operator has been overloaded which is easy to write in C++, then too bad - the overload doesn't short circuit.
I think if you can't figure out a way to preserve the short-circuit feature then having a way to overload this operator in your language is stupid. It is, I would say, unsurprising to me that C++ did it anyway.
EtA:: It feels like in say Rust you could pull this off as a trait LogicalAnd implemented on a type Foo, with a method that takes two parameters of type FnOnce() -> Foo , and then the compiler turns (some_complicated_thing && different_expression) where both of the sub-expressions are of type Foo into something like:
{
let a = (|| some_complicated_thing);
let b = (|| different_expression);
LogicalAnd::logical_and(a, b)
}
To deliver the expected short-circuiting behaviour in your implementation of the logical_and method, you just don't execute b unless you're going to care about the result.And in any case the author misidentified the problem and solution. The problem is that C++ coerces bool to int. I'm 99% sure there's a warning for that that you can turn into an error.
I'm just saying that in this particular case, sometimes the compiler will be able to optimise it but you can't rely on it.
bool cheap = cheapCheck();
bool expensive = cheap && expensiveCheck();
if (expensive) { ... }