Most operators give no guarantee as to the order in which operands will be evaluated (§ 4.1.3, p. 137). This lack of guaranteed order often doesn’t matter. The cases where it does matter are when one subexpression changes the value of an operand that is used in another subexpression. Because the increment and decrement operators change their operands, it is easy to misuse these operators in compound expressions.
To illustrate the problem, we’ll rewrite the loop from § 3.4.1 (p. 108) that capitalizes the first word in the input. That example used a for
loop:
for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
*it = toupper(*it); // capitalize the current character
which allowed us to separate the statement that dereferenced beg
from the one that incremented it. Replacing the for
with a seemingly equivalent while
// the behavior of the following loop is undefined!
while (beg != s.end() && !isspace(*beg))
*beg = toupper(*beg++); // error: this assignment is undefined
results in undefined behavior. The problem is that in the revised version, both the left- and right-hand operands to =
use beg
and the right-hand operand changes beg
. The assignment is therefore undefined. The compiler might evaluate this expression as either
*beg = toupper(*beg); // execution if left-hand side is evaluated first
*(beg + 1) = toupper(*beg); // execution if right-hand side is evaluated first
or it might evaluate it in yet some other way.
Exercise 4.17: Explain the difference between prefix and postfix increment.
Exercise 4.18: What would happen if the while
loop on page 148 that prints the elements from a vector
used the prefix increment operator?
Exercise 4.19: Given that ptr
points to an int
, that vec
is a vector<int>
, and that ival
is an int
, explain the behavior of each of these expressions. Which, if any, are likely to be incorrect? Why? How might each be corrected?
(a) ptr != 0 && *ptr++
(b) ival++ && ival
(c) vec[ival++] <= vec[ival]