Valid XHTML 1.1

Firefox Download Button

Conditional Execution in C++

Carlos Moreno

I'll answer any ten questions that call for a yes or a no
– Q (from Star Trek, not Bond!)

The if Statement

Quite often we need to control the flow of execution of a program; in particular, to decide if a certain instruction or block of instructions should be executed, depending on a given condition. For example, in a more advanced version of our invoice program, we may want to take into account the fact that some products are taxable and some are not. The program could ask the user whether the product is taxable or not, and depending on what the user indicates, calculate things accordingly.

The most fundamental statement related to these types of situations is the if statement. The syntax for the if statement is as follows:

if (condition)
{
    statement(s);
}

A simple concrete example is shown below:

if (number < 0)
{
    cout << "Error! You must enter a positive number!" << endl;
}

This works as follows: the value of the variable number is retrieved, and compared against 0. If it is less than 0, then the cout statement (plus any other statements enclosed between the curly braces) is executed, and then execution continues at the statement following the closing curly brace (not shown in the example above, but presumably there are additional statements). If it is not less than 0, then the cout statement is skipped, and execution continues at the statement following the closing curly brace.

The round brackets are not optional. The curly braces are optional only if it is a single statement what executes conditionally. If more than one statement is associated to the if, then you must enclose the statements between curly braces.

In the case of a single statement, you may still enclose the statement in curly braces; this is a matter of style, but it is my opinion and my advice that one should always and unconditionally use curly braces to enclose the statement or statements associated to an if statement.

The reason is quite simple: if you do not use them, and in the future need to add an additional statement (it could even be a cout statement for debugging purposes, in a situation where you suspect that there may be some problem around the if or the condition), you could easily end up making the following mistake:

Original code:

if (number < 0)
    cout << "Error! You must enter a positive number!" << endl;

And you decide that your program should fix the condition and warn the user, instead of refusing to do anything and print an error message:

if (number < 0)
    cout << "Negative number entered -- adjusting sign" << endl;
    number = -number;

Even though we may be misled by the indentation (the nice alignment to the right for the whole block presumably associated to the if), the compiler is not — remember that the compiler ignores extra spaces and newlines between tokens or statements; and because there are no curly braces, the rule says that only the statement immediately following the if and the condition is the one executed conditionally. The statement number = -number; is not part of the if, and is simply the next statement to be executed — unconditionally!

The above can not happen if we always use curly braces, even when it is a single statement.

Important: the if statement must NOT have a semicolon at the end of the line (i.e., after the closing bracket for the condition). This is one of the common mistakes that can give you a really hard time before identifying it and fixing it!

This is how it works. If you code the following:

if (number < 0);
{
    cout << "Negative number entered -- adjusting sign" << endl;
    number = -number;
}

The compiler sees that there is a statement after the closing bracket for the condition. It is a special case of statement: a null statement; one that has no effect. But it is a statement that finishes at the semicolon. Therefore, the entire block that follows is simply a normal block of code that executes after the if statement — it executes unconditionally and independently of the if statement.

Firefox Download Button

Conditions

Conditions are expressions that evaluate to a boolean value — a true or false value (true and false are C++ keywords, representing the two possible values of a boolean expression or variable). Simple conditions involve two operands, each of which can be a variable or a literal value, and an operator, typically a comparison operator.

The comparison operators are shown below:

== true if and only if left operand is equal to right operand
!= true if and only if left operand is not equal to right operand
> true if and only if left operand is greater than right operand
< true if and only if left operand is less than right operand
>= true if and only if left operand is greater than or equal than right operand
<= true if and only if left operand is less than or equal than right operand

The examples below illustrate the use of conditions in an if statement:

if (price <= 0)
{
    // Error condition
}

if (price > average_price)
{
    // Apply some discount
}

if (age < 18)
{
    // Error message -- not allowed to buy alcohol
}

if (age >= 65)
{
    // Apply seniors' discount
}

The operands can be arbitrary expressions, like in the example shown below:

if (a*a + 2*b >= c / (d+1))

Boolean Variables

A condition may be given directly by a boolean variable. This should be quite clear from the fact that a variable of type bool can only hold the values true or false. In essence, a boolean variable is an expression (a very simple expression) that evaluates to true or false, so it is a particular case of a condition.

We could take advantage of boolean variables in a situation where we need to avoid repeated evaluation of a condition that is complicated and takes many operations. For example, let's say that our program needs to evaluate the following mathematical expressions:

x1 =
a2 + b2
 x2 − 2 (y12 + y22) + y2 
x2 =
a2 − c2
 x2 − 2 (y12 + y22) + y2 
x3 =
2 a2 + 3 b2
 x2 − 2 (y12 + y22) + y2 

For each of the expressions, we need to check that we don't attempt a division by 0. But we notice that for the three cases, we divide by the same value; so, evaluating three times that complicated expression to compare it against zero is unnecessary. We could evaluate the condition only once, and store the result (remember that the result is a true / false value) in a boolean variable that may be used later as a condition, as shown below:

const bool not_div_by_0 = x*x - 2*(y1*y1 + y2*y2) + y*y != 0;

if (not_div_by_0)
{
    x1 = (a*a + b*b) / (x*x - 2*(y1*y1 + y2*y2) + y*y);
}
// other statements required before computing x2 and x3
if (not_div_by_0)
{
    x2 = (a*a - c*c) / (x*x - 2*(y1*y1 + y2*y2) + y*y);
}
// other statements required before computing x3
if (not_div_by_0)
{
    x3 = (2*a*a + 3*b*b) / (x*x - 2*(y1*y1 + y2*y2) + y*y);
}

The above is just a silly example — it would be better to evaluate the result of the complicated expression in the denominator and check if that value is different from zero; that way we would optimize the computation of x1, x2, and x3, since the common part of the three formulas is already pre-computed. Still, the above is intended as an example of how to avoid repeated evaluation of a condition by storing the result of the condition in a boolean variable; using the value of the boolean variable as a condition is much more efficient than having to re-compute the complicated expression.

An additional benefit is that it can help improve readability; the same issues discussed for named constants apply in here — we are giving a meaningful name to the otherwise long and complicated condition, making it easier to read and understand.

We could also use the NOT operator (!), which negates the value of the boolean expression:

const bool div_by_0 = x*x - 2*(y1*y1 + y2*y2) + y*y == 0;

if (! div_by_0) // we read this as: if not div_by_0
{
    // .... etc.

The space between ! and div_by_0 is optional, but I normally recommend to use it, since it increases readability — if we omit it, the ! could be easily missed when quickly looking at the expression.

Composite Conditions

Very often, we encounter situations where the condition can not be expressed as the simple conditions from the previous section, just by comparing two values. An example of such situations is testing if a number is within a given range of values; for instance, testing if a number is between 0 and 10.

This example involves testing two conditions, and verifying if the two conditions are simultaneously met. In other words, we want to test if the number is greater than or equal to 0 and also less than or equal to 10.

To this end, we use logical operators to combine two conditions. In the above example, we use the logical AND operator — &&, as shown below:

if (0 <= number && number <= 10)

Notice that, even though the first part may look a little strange, there is nothing wrong with it — each operand around a comparison operator may be an arbitrary expression, so there is nothing that prevents us from writing a numeric constant on the left and a variable on the right. I wrote this condition this way to make it look like the equivalent mathematical notation, 0 ≤ n ≤ 10.

However, notice that we can not write (0 <= number <= 10), because the comparison operators (in this case, <=) require two compatible expressions (in this case, two expressions that evaluate to a numeric value) on each side. If we tried to write the expression in this compact form, the second operator <= would not have a numeric expression on its left, but a boolean expression, and we would be asking the program to compare if a true/false value is less than or equal than 10.

Also, we can not write it the way we say it in English (or I presume, in most spoken languages), which would be “if the number is greater than 0 and less than 10”. This works in spoken languages because the subject in each sub-sentence is implicitly assumed to be the same. In programming languages, things do not work that way — computers are too literal and too structured for these kinds of linguistic subtleties.

The rule is simple: the logical AND operator requires two expressions that are valid conditions on each side. If we write number >= 0 && <= 10, the expression on the right of && is not a valid condition.

The same applies to the logical OR operator, which in C++ is written as two pipe characters, as shown in the example below:

if (answer == 'y' || answer == 'Y')

In this example, we use a composite condition to check, in a case-insensitive way, if the variable answer contains a y.

The NOT operator can also be used for arbitrary logical expressions; it is not restricted to be used with boolean variables. For example, the following if statement is perfectly valid:

if (! (answer == 'q' || answer == 'Q'))

The above could be seen as checking if the user did not select the code to Quit the application. (Try writing the above expression without using the NOT operator! You may be surprised by how complicated it may be to get it right — if you don't believe me, try it! And do make sure that you get it right!)

Short-circuit Evaluation

In C++, the logical operators && and || have an interesting property: short-circuit evaluation. The language rules require that when evaluating a composite condition, if the result from the first part (the expression on the left of the logical operator) is sufficient to determine the result of the condition, then the second part must not be evaluated.

This is a stronger requirement than simply an optimization issue; when evaluating the condition such as 0 <= number && number <= 10, it seems a reasonable optimization that if the number is negative, then as soon as the first part of the condition is evaluated, we do not need to evaluate the second part to know what is the final result — if the first part of the condition evaluates to false, then the requirement that both be true will also evaluate to false, and so, we do not need to make the processor waste time in evaluating something that will not make a difference in the result. The same applies to the logical OR operator (||) when the first part of the expression evaluates to true; at this point, the result of the whole condition is known with absolute certainty, regardless of the second part of the condition.

In C++, the rule goes beyond the above reasoning — it's not that the compiler doesn't need to evaluate the second part in these cases; it is not allowed to do it, and there are situations where we rely on this requirement, such as the following:

if (b != 0 && a / b > 0)

In this case, we want to make sure that we only divide by b if the value of b is not 0. In other languages where we don't have short-circuit evaluation of logical expressions, the above would be incorrect, since the compiler would still evaluate both conditions to then apply a logical AND of the two results — but merely attempting to evaluate the second part causes an error (division by 0). With short-circuit evaluation, we know that a division by 0 can not be even attempted, since the first condition evaluating to false (which would happen if b is 0) prevents the compiler from evaluating the second part.

The if-else Statement

The simple if statement covers the cases where we have a fragment of code that should be executed depending on a condition. If we have a situation where we have two possible actions, and we want to do one or the other, depending on a given condition, we use the if – else statement.

The syntax for the if – else statement is:

if (condition)
{
    // block 1
}
else
{
    // block 2
}

In the above, if the condition is true, then block 1 is executed, and then execution continues after the end of block 2; if the condition is false, then block 2 is executed (block 1 is skipped), and then execution continues after the end of block 2.

An extension of the above idea is the else if statement. Strictly speaking, there is no such thing as an else if statement in C++, and it is simply a nested if following the else part of the first statement. The generic syntax is as follows:

if (condition 1)
{
    // block 1
}
else if (condition 2)
{
    // block 2
}
...
else if (condition N)
{
    // block N
}
else
{
    // Optional block to be executed if all of the
    // previous conditions evaluated to false
}

Notice that only one of the blocks will be executed. This includes the block corresponding to the optional else part — if one of the previous blocks executes, then the rest is skipped, and execution continues after the end of the else block, or after the last else if block, in the cases where there is no else.

One example where the use of else if provides a convenient solution is checking multiple ranges for a value. For example, if we want to convert a final grade in a scale of 0 to 100 to a letter grade (for example, 90 to 100 corresponds to an A, 80 to 89 is B, 70 to 79 is C, 60 to 69 is D, and less than 60 is F), we could use the following fragment of code:

if (grade >= 90)
{
    letter_grade = 'A';
}
else if (grade >= 80)
{
    letter_grade = 'B';
}
else if (grade >= 70)
{
    letter_grade = 'C';
}
else if (grade >= 60)
{
    letter_grade = 'D';
}
else
{
    letter_grade = 'F';
}

At each stage, the else part is executed only if the previous conditions were false, so we implicitly have the double condition (for example, when checking grade >= 80, we know that grade is less than 90, since we are in the else part of the if (grade >= 90)).

Firefox Download Button

The switch Statement

The switch statement provides a convenient mechanism to execute one out of several possible fragments of code, depending on the value of an integral variable or expression.

A typical example that illustrates the use of the switch statement is handling a menu selection. In a text-based interface, a program could present the user with a menu of options (say option 1 to option 3), and prompt the user to select an option. Then, depending on the value that the user entered, the program should execute the corresponding fragment of code, among the five possible choices.

In this case, a solution using a switch statement would look like the following:

int choice;
cin >> choice;
 
switch (choice)
{
    case 1:
        cout << "First item selected!" << endl;
        break;
 
    case 2:
        cout << "Second item selected!" << endl;
        break;
 
    case 3:
        cout << "Third item selected!" << endl;
        break;
 
    default:
        cout << "Invalid selection!" << endl;
        break;  color{DarkGreen}// See note in the text
}

The original rationale behind the switch statement was to provide an efficient mechanism for conditional execution where one block out of several possible blocks would be executed depending on the value of a given expression. This expression was limited to integral types so that the compiler could implement the block as a jump table — something that could be implemented efficiently on most processors.

The flow of execution goes as follows: the line switch (expression) causes execution to continue in the case that has the same value as the given expression. From that point, execution continues all the way through the rest of the statement block. For this reason, the break statements are necessary, to restrict the executed code; the break statement causes execution to continue after the body of the switch statement (as intuition would seem to dictate).

The default clause is chosen if the value of the expression does not appear in any of the case clauses.

Notice that the last block does not require a break statement, since execution will continue after the body of the switch statement anyway. However, it does not hurt or affect the code in any way, so I always recommend that you do use a break statement at the end of each block, as a matter of habit. This has two advantages: if you develop the habit of always placing a break statement at the end of each block, you won't accidentally forget to place one that is needed. And also, you avoid the risk that later, during maintenance tasks, someone (you or some other programmer working on the same project) adds an additional case clause, or a default clause if there were none, and overlooks to add the now necessary break in the block that no longer is the last.

The Ternary Operator ?:

The ternary operator provides a compact and convenient way to write an expression that yields two possible values, depending on a given condition. The generic syntax of this operator is as follows:

condition ? expression1 : expression2

The above is an expression that evaluates to either the result of expression1 or the result of expression2 — if the condition is true, then the expression evaluates to the result of expression1; otherwise it evaluates to the result of expresssion2.

One example where this can be very convenient is when trying to initialize a variable in its declaration (and possibly make it const-qualified) with a value that depends on certain condition.

For example, in the case of the tax calculation for the invoice example, suppose that you have a provincial tax rate that is a certain rate for all the provinces except for province X. To simplify the example, let's assume that the program holds the province as a one-letter code, stored in a variable of type char. As an example, let's assume that the provincial tax rate is 7% for all provinces, and 8% for province X. We could still declare a named constant containing the right value according to the province:

const double pst_rate = province == 'X' ? 0.08 : 0.07;

Notice that we would not be able to declare a const-qualified pst_rate with a value that depends on the contents of the variable province. If we use an if – else statement, the scope of the declarations (the area of the code where the declaration is visible) would be restricted to inside the if or the else blocks. And if we assign one value and then check the condition to assign the other value, then we can not make it const-qualified.

  Firefox Download Button