I have some logic that looks like this:
if (x > y || eval(x, y)) {
// do something
}
I don't want eval(x, y)
(this is a boolean function) to be compiled when some condition (known at compile time is met) is met (in this case we can just get rid of the ||
too, or for uniformity, we could just have eval(x, y)
replaced with false
).
Is there a way to achieve this?
I think one way to do this is
bool rhs = false;
if constexpr ( condition ) {
rhs = eval(x, y);
}
if (x > y || rhs) {
// do something
}
but then I lose the benefit of short circuiting here. I don't want to call eval(x, y)
until x > y
is false.
CodePudding user response:
You can wrap it with lambda
auto maybe_eval = [](auto x, auto y) {
if constexpr (some condition)
return false;
else
return eval(x, y)
};
if (x > y || maybe_eval(x, y)) {
// do something
}
CodePudding user response:
Short-circuit eval of false && eval(x,y)
will make that call into dead code.
康桓瑋 points out that short-circuit evaluation does not work when some_condition makes eval(x,y)
ill-formed; in that case see their answer for a lambda. In this answer, it still has to be able to compile, but it compiles to no asm.
void foo(int x, int y) {
if (x>y || (condition && eval(x,y))) {
sink = 1; // store to a volatile global is easy to see in compiler asm output
}
}
- With
condition = false
, it's the same asif(x>y || false)
, i.e.if(x>y)
. - With
condition = true
, it's the same asif(x>y || eval(x,y))
Real compilers do in practice do this short-circuit eval to omit any call instruction to eval
. For GCC and clang at least, that dead code removal happens even with optimization disabled. See it on the Godbolt compiler explorer with GCC and clang. With condition = true
, this function compiles to the same asm as the simple original using if (x > y || eval(x, y))
. With condition = false
it simplifies it to only check the x > y
condition, as if eval
always returned 0.
Fun fact: with __attribute__((const)) int eval(int, int);
or pure
, clang will even optimize your if constexpr
attempt into the same asm, since it knows that the call doesn't have side effects so it's ok to not call it.