That depends. In some cases the compiler will be able to determine that `expensiveCheck()` has no side effects and make them equivalent. But you can't really rely on it.
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.
That's a different thing - the "sufficiently smart compiler" is postulating that it doesn't matter if a language is not very high performance because somehow magically there will one day be a compiler that can just figure out how to optimise the code in every case.
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.
I think the idea is that if there's observable behavior in unconditionally calling the expensive function, the compiler can't skip it without altering the program behavior. Whereas if they're called as part of the if condition, the compiler can always safely skip the second function (and it's documented that it must do so, due to "short circuit" semantics) if the first function is false.
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.
(Fun fact: I read meindnoch's comment right after I woke up and I, too, was about to reply to them with a godbolt listing to show that it would compile to the same thing, only to realize I completely forgot about short-circuit semantics. So you're not alone!)
> 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
Note that (CPP reference will also tell you) this only applies to the built-in operator.
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.