Short-circuit Evaluation
Learn how the &&
and ||
operators are optimized with short-circuit evaluation, only evaluating expressions when necessary
When we combine boolean values using the AND (&&
) and OR (||
) operators, C++ and most other programming languages are smart about how it evaluates them. It performs what's known as short-circuit evaluation.
This means that the second operand in a logical expression is only evaluated if its value is absolutely necessary to determine the final result. We'll walk through some examples in this lesson.
This short-circuiting behavior is not just a performance optimization - we can also use it to control the flow of our code, much like we'd use if
and else
statements.
For example, imagine we have two tasks that must be performed in sequence, but the second task should only be attempted if the first one succeeds. We could write this using an if
statement, which works perfectly fine:
#include <iostream>
using namespace std;
bool TaskOne() {
cout << "Performing task one...\n";
// ...
cout << "Something went wrong!\n";
return false;
}
bool TaskTwo() {
cout << "Performing task two...\n";
// ...
cout << "Success!\n";
return true;
}
int main(){
bool TaskOneComplete{TaskOne()};
if (TaskOneComplete) {
TaskTwo();
}
}
Performing task one...
Something went wrong!
However, by using the short-circuiting behavior of the &&
and ||
operators, we can write sequential dependencies like this in a much simpler way.
Example 1: Using &&
In an expression like TaskOne() && TaskTwo()
, if TaskOne()
evaluates to false
, TaskTwo()
is never evaluated, meaning TaskTwo
is not invoked.
The reason for this is that it doesn't matter what TaskTwo()
returns - it is not going to change the value returned by the &&
operator.
After TaskOne()
returns false
, the expression is equivalent to false && TaskTwo()
. The expression false && anything
will return false
regardless of the value of anything
, so our program won't even check what anything
is.
The following program shows this in action:
#include <iostream>
using namespace std;
bool TaskOne() {/*...*/}
bool TaskTwo() {/*...*/}
int main(){
bool TasksCompleted{TaskOne() && TaskTwo()};
if (!TasksCompleted) {
cout << "Failed to complete the tasks";
}
}
Note how TaskTwo()
isn't executed if TaskOne()
returns false
:
Performing task one...
Something went wrong!
Failed to complete the tasks
We can extend this pattern to any number of evaluations. The following expression will continue until one of the tasks returns false
:
bool TasksCompleted{
TaskOne() &&
TaskTwo() &&
TaskThree() &&
TaskFour()
};
Test your Knowledge
Short-Circuit Evaluation
What will the following program log out?
#include <iostream>
using namespace std;
bool ShouldLog(int x) {
return x > 5;
}
int main(){
int Value{3};
ShouldLog(Value) && cout << Value;
}
Example 2: Using ||
The same logic applies to the ||
operator. It doesn't evaluate operands unnecessarily - as soon as knows the result of the overall expression, it returns it.
This means that, in an expression like OptionOne() || OptionTwo()
, if OptionOne()
returns true
, OptionTwo()
is never evaluated.
This is because it doesn't matter what OptionTwo()
will return - if OptionOne()
was true, the overall expression is going to be true
. In other words, true || anything
is true
regardless of the value of anything
.
In the following example, we provide two options to complete a task. The program will stop as soon as it finds one that works:
#include <iostream>
using namespace std;
bool OptionOne() {
cout << "Trying option one...\n";
// ...
cout << "Success!\n";
return true;
}
bool OptionTwo() {
cout << "Trying option two...\n";
// ...
cout << "Success!\n";
return true;
}
int main(){
bool WorkDone{OptionOne() || OptionTwo()};
if (WorkDone) {
cout << "Work completed successfully";
}
}
Note how OptionTwo()
isn't called if OptionOne()
returns true:
Trying option one...
Success!
Work completed successfully
Summary
In this lesson, we've learned about short-circuit evaluation, a key feature of logical operators that makes our code more efficient and concise.
&&
(AND) stops evaluating as soon as it finds afalse
operand. The expressionA && B
will not evaluateB
ifA
is false.||
(OR) stops evaluating as soon as it finds atrue
operand. The expressionA || B
will not evaluateB
ifA
is true.- Benefits: This behavior allows us to write more performant code by avoiding unnecessary function calls, and gives us a concise way of creating sequential conditions in a single expression.
Abstraction and Classes
Learn how to define, instantiate, and utilize classes, understanding how they form the backbone of object-oriented programming.