How are conditions checked?

Will the second part of an AND condition in an if statement be checked if the first one is false. So for example is there a difference in optimization in these these two ways of checking.

1
2
3
4
5
6
7
8
9
10
11
12
  if(cheap_check() and expensive_check())
  {
     do_something();
  }
///////////////////////////
  if(cheap_check())
  {
     if(expensive_check())
     {
        do_something();
     }
  }
There is no difference, for built-in types. This is referred to as "short-circuiting" the logic.

The only difference is when the && operator is overloaded for a custom class; custom logical operators don't short-circuit. I do not recommend such [ab]use of such operator overloads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Example program
#include <iostream>

struct Foo {   
    operator bool() const { return false; }
};

Foo cheap_check()
{
    std::cout << "Cheap\n";
    return Foo{};   
}

Foo expensive_check()
{
    std::cout << "Expensive\n";
    return Foo{};
}

// not recommended! Confusing!
bool operator&&(const Foo& fooL, const Foo& fooR)
{
    return static_cast<bool>(fooL) && static_cast<bool>(fooR);
}

void do_something()
{
    std::cout << "Something\n";   
}

int main()
{
    std::cout << "Test 1:\n";
    if(cheap_check() and expensive_check())
    {
        do_something();
    }
    
    std::cout << "\n\nTest 2:\n";
    if(cheap_check())
    {
        if(expensive_check())
        {
            do_something();
        }
    }
}

Possible output:
Test 1:
Expensive
Cheap


Test 2:
Cheap


Aside: This also ties into another common "gotcha" in C++: Order of evaluation.
https://en.cppreference.com/w/cpp/language/eval_order
It is unspecified whether the above program prints "Expensive" or "Cheap" first. Avoid nasty side effects in conditionals. Write clear code, not clever code.
Last edited on
For the built-in logical AND operator, the result is true if both operands are true. Otherwise, the result is false. This operator is short-circuiting: if the first operand is false, the second operand is not evaluated.
https://en.cppreference.com/w/cpp/language/operator_logical
Will the second part of an AND condition in an if statement be checked if the first one is false.

When using && and || (or and and or) the second operand is only evaluated if necessary to compute the result.

That's what happens conceptually.

So for example is there a difference in optimization in these these two ways of checking.

Unlikely.

What the compiler does, whether or not it will optimize, is up to the compiler to decide as long as the observable behaviour does not change (how long it takes for your code to run does not count as observable behaviour).

You don't have to worry that it will run expensive_check() if cheap_check() is false. It would only be able to do that if it can prove that it doesn't actually change the behaviour of the program (because conceptually it was never called) but compilers are not intentionally trying to be evil so the only reason it would ever try to always run both functions would be if it thought it was actually faster (one branch instead of two could be worth a little bit of extra work).
Last edited on
So for example is there a difference in optimization in these these two ways of checking.

What the compiler does, whether or not it will optimize, is up to the compiler to decide as long as the observable behaviour does not change (how long it takes for your code to run does not count as observable behaviour).


In this case, however, the "short-circuiting" of the logical operators is not just an optional compiler-optimization, but is an observable behavior that is guaranteed by the standard (the second operand may have some side side-effects, which only occur if it is actually evaluated; and we must be able to rely on that).

So, the "two ways of checking" are two different ways of writing down the exactly same logic/behavior.

________

For example, in the following code (which actually is safe) we must 100% rely on the fact that the second operand will never be evaluated if the first one was false, or otherwise our program may crash:
1
2
3
4
5
FILE *file = fopen("myfile.txt","r");
if ((file != NULL) && (fread(buffer, 42, 1, file) == 1))
{
    /* ... */
}
Last edited on
Topic archived. No new replies allowed.