japreiss
japreiss

Reputation: 11251

Can C/C++ optimizers decide to lazily evaluate values that are only used in a short-circuit evaluation?

I like to refactor complicated conditionals like this:

if (foo(blah) || (bar(param1, param2, param3) && !obj.longMethodName())) ...

into this:

bool foo_true = foo(blah);
bool bar_true = bar(param1, param2, param3);
bool long_true = obj.longMethodName();

if (foo_true || (bar_true && !long_true)) ...

I think this makes the code a lot easier to understand, and it helps with debugging because you can see the intermediate results used to compute the final condition.

But: in the original code, due to short circuiting, bar will only be evaluated if foo returns true, and longMethodName only if bar returns true.

Assume that functions are "pure", methods are const, and the compiler can see the function bodies of everything. Are C++ optimizers allowed to defer evaluating my intermediate values until they are needed?

Upvotes: 1

Views: 152

Answers (5)

James Kanze
James Kanze

Reputation: 153909

Of course. Provided the compiler can see enough to determine that foo, bar and obj.longMethodName() don't have any other impact on the observable behavior of your code.

Whether any compilers do is another question. I rather doubt it; it would require some very special logic, which isn't in the usual list of optimization techniques, for something that practically nobody does. (I suspect that most programmers would find the original version, formatted correctly, to be more readable than the one with a lot of extra variables.)

EDIT:

I wonder if it's worth pointing out that the compiler is allowed to call all three functions even if the if is written:

if ( foo( blah ) || (bar( p1, p2, p3 ) && ! obj.lMN() ) )

(Although I can't imagine one that would.) The standard makes no requirements with regards to which functions are called when; it only requires that the observable behavior be the same (same values and in the same order---no guarantees with regards to time) "as if" the formal semantics of the program were followed. And the only things that count as observable behavior is IO (in some form) and accesses to volatile objects.

Upvotes: 4

luk32
luk32

Reputation: 16070

I am not sure if the assignment would be counted as a side-effect already. To say the least it is probably hard to determine if it is safe to move the actual call.

But, I would like to point out that in c++11 it is possible to achieve what OP pursues with nearly exact same syntax that OP uses in examples utilizing std::bind.

It is just that foo_true, would not be defined as

bool foo_true = foo(blah);

but rather

auto foo_true = std::bind(foo, blah).

The if could be then checked as if( foo_true() || bar_true() ).

Whether it is cleaner or not, is up to personal matter IMO. But I believe it behaves as both wanted, and expected. Full code:

#include <iostream>
#include <functional>

using namespace std;

bool foo(int blah){
    cout << "blah: " << blah << '\n';
    return blah;
}

bool bar(bool negate_me){
    cout << "negate_me: " << negate_me << '\n';
    return !negate_me;
}

int main() {
    bool test = true;
    int param = 42;
    auto foo_true = std::bind(foo, test);
    auto bar_true = std::bind(bar, param);

    if (foo_true() || bar_true() ) cout << "TEST\n";
    return 0;
}

Output:

blah: 1
TEST

bar wasn't called. Change test to false and it will be.

Upvotes: 0

doron
doron

Reputation: 28872

The problem here is that foo and bar could be implemented in another compilation unit and C++ does not have the concept of function purity. This means that foo and bar might have side effects (changes to screen or global variables) and therefore must be evaluated in order for you to get expected behaviour.

Interestingly enough, with GCC, functions can be declared with the pure attribute. This tells the compiler that the function does not have any side effects. And therefore can be called lazily. See here for more details.

Upvotes: 0

QuestionC
QuestionC

Reputation: 10064

No. C++ has no concept of a pure method with no side effects, so there really isn't a way to optimize that.

Upvotes: 0

Sergey L.
Sergey L.

Reputation: 22542

No. You compiler is not allowed to make the optimisation because it can not determine wether you have meant the short circuit or wether you want to have a potential side effect of evaluating bar no matter what.

Upvotes: 1

Related Questions