Skip to content

4.3. Logical and Relational Operators

The relational operators take operands of arithmetic or pointer type; the logical operators take operands of any type that can be converted to bool. These operators all return values of type bool. Arithmetic and pointer operand(s) with a value of zero are false; all other values are true. The operands to these operators are rvalues and the result is an rvalue.

Table 4.2. Logical and Relational Operators

AssociativityOperatorFunctionUse
Left<Less than-expr
Left<=Less than or equalexpr + expr
Left>Greater thanexpr - expr
Left>=Greater than or equalexpr * expr
Left==Equalityexpr / expr
Left!=Inequalityexpr % expr
Right!Logical NOT+expr
Left&&Logical ANDexpr % expr
Left||Logical ORexpr % expr

Logical AND and OR Operators

The overall result of the logical AND operator is true if and only if both its operands evaluate to true. The logical OR (||) operator evaluates as true if either of its operands evaluates as true.

The logical AND and OR operators always evaluate their left operand before the right. Moreover, the right operand is evaluated if and only if the left operand does not determine the result. This strategy is known as short-circuit evaluation:

  • The right side of an && is evaluated if and only if the left side is true.
  • The right side of an || is evaluated if and only if the left side is false.

Several of the programs in Chapter 3 used the logical AND operator. Those programs used the left-hand operand to test whether it was safe to evaluate the right-hand operand. For example, the for condition on page 94:

c++
index != s.size() && !isspace(s[index])

first checks that index has not reached the end of its associated string. We’re guaranteed that the right operand won’t be evaluated unless index is in range.

As an example that uses the logical OR, imagine we have some text in a vector of strings. We want to print the strings, adding a newline after each empty string or after a string that ends with a period. We’ll use a range-based for loop (§ 3.2.3, p. 91) to process each element:

c++
//  note s as a reference to const; the elements aren't copied and can't be changed
for (const auto &s : text) { // for each element in text
    cout << s;        // print the current element
    // blank lines and those that end with a period get a newline
    if (s.empty() || s[s.size() - 1] == '.')
        cout << endl;
    else
        cout << " ";  // otherwise just separate with a space
}

After we print the current element, we check to see if we need to print a newline. The condition in the if first checks whether s is an empty string. If so, we need to print a newline regardless of the value of the right-hand operand. Only if the string is not empty do we evaluate the second expression, which checks whether the string ends with a period. In this expression, we rely on short-circuit evaluation of || to ensure that we subscript s only if s is not empty.

It is worth noting that we declared s as a reference to const2.5.2, p. 69). The elements in text are strings, and might be large. By making s a reference, we avoid copying the elements. Because we don’t need to write to the elements, we made s a reference to const.

Logical NOT Operator

The logical NOT operator (!) returns the inverse of the truth value of its operand. We first used this operator in § 3.2.2 (p. 87). As another example, assuming vec is a vector of ints, we might use the logical NOT operator to see whether vec has elements by negating the value returned by empty:

c++
// print the first element in vec if there is one
if (!vec.empty())
    cout << vec[0];

The subexpression

c++
!vec.empty()

evaluates as true if the call to empty returns false.

The Relational Operators

The relational operators (<, <=, >, <=) have their ordinary meanings and return bool values. These operators are left associative.

Because the relational operators return bools, the result of chaining these operators together is likely to be surprising:

c++
// oops! this condition compares k to the bool result of i < j
if (i < j < k) // true if k is greater than 1!

This condition groups i and j to the first < operator. The bool result of that expression is the left-hand operand of the second less-than operator. That is, k is compared to the true/false result of the first comparison! To accomplish the test we intended, we can rewrite the expression as follows:

c++
// ok: condition is true if i is smaller than j and j is smaller than k
if (i < j && j < k) { /* ...  */ }

Equality Tests and the bool Literals

If we want to test the truth value of an arithmetic or pointer object, the most direct way is to use the value as a condition:

c++
if (val)  { /*  ...  */ } // true if val is any nonzero value
if (!val) { /*  ...  */ } // true if val is zero

In both conditions, the compiler converts val to bool. The first condition succeeds so long as val is nonzero; the second succeeds if val is zero.

We might think we could rewrite a test of this kind as

c++
if (val == true) { /* ...   */ } // true only if val is equal to 1!

There are two problems with this approach. First, it is longer and less direct than the previous code (although admittedly when first learning C++ this kind of abbreviation can be perplexing). Much more importantly, when val is not a bool, this comparison does not work as expected.

If val is not a bool, then true is converted to the type of val before the == operator is applied. That is, when val is not a bool, it is as if we had written

c++
if (val == 1) { /* ... */ }

As we’ve seen, when a bool is converted to another arithmetic type, false converts to 0 and true converts to 12.1.2, p. 35). If we really cared whether val was the specific value 1, we should write the condition to test that case directly.

WARNING

It is usually a bad idea to use the boolean literals true and false as operands in a comparison. These literals should be used only to compare to an object of type bool.

INFO

Exercises Section 4.3

Exercise 4.8: Explain when operands are evaluated in the logical AND, logical OR, and equality operators.

Exercise 4.9: Explain the behavior of the condition in the following if:

c++
const char *cp = "Hello World";
if (cp && *cp)

Exercise 4.10: Write the condition for a while loop that would read ints from the standard input and stop when the value read is equal to 42.

Exercise 4.11: Write an expression that tests four values, a, b, c, and d, and ensures that a is greater than b, which is greater than c, which is greater than d.

Exercise 4.12: Assuming i, j, and k are all ints, explain what i != j < k means.