Consider this code:
bool res_true();
bool res_false();
bool test1(const bool cond)
{
return (cond && res_true()) || (!cond && res_false());
}
bool test2(const bool cond)
{
return cond ? res_true() : res_false();
}
Both test1()
and test2()
select a certain boolean result between res_true(),res_false()
basing on a boolean input cond
.
I know that c++ compilers nowadays can be pretty smart, but I usually tended to use the test1()
form -despite less readable- because in my naivety I thought that in that case there were just logic boolean operations, with no conditional jumps; they are bad, right?
Well today I tried that in compiler explorer and saw that the generated output is very similar for both the functions: test1()
contains a conditional jump (moreover, depending on compiler and enabled optimizations, the generated assembler of test1()
can be worst than the one of test2()
), why is that?
That's because the default c++ boolean operators ||
and &&
are short-circuited (with guaranteed left-to-right evaluation), so a logical expression may or may not be fully evaluated depending on the results of his sub-expressions.
for example in:
f() || g()
f()
will be called first and g()
won't be called if f()
was true
; similarly in:
f() && g()
g()
won't be called if f()
was false
, so the generated assembler must contain a conditional jump to ensure to short-circuit the expression.