E 4.1 Fundamentals: C H A P T E R
E 4.1 Fundamentals: C H A P T E R
C H A P T E R 4 plicated expressions are formed from an operator and one or more operands.
4.1 Fundamentals
EXPRESSIONS
There are a few fundamental concepts that affect how expressions are evaluated.
We start by briefly discussing the concepts that apply to most (if not all) expres-
sions. Subsequent sections will cover these topics in more detail.
The operands to the * operator could be 10 and 20, or 10 and 20/2, or 15 and 20,
or 15 and 20/2. Understanding such expressions is the topic of the next section.
C++ provides a rich set of operators and defines what these operators
Operand Conversions
do when applied to operands of built-in type. It also allows us to de-
fine the meaning of most of the operators when applied to operands As part of evaluating an expression, operands are often converted from one type
of class types. This chapter focuses on the operators as defined in the to another. For example, the binary operators usually expect operands with the
language and applied to operands of built-in type. We will also look same type. These operators can be used on operands with differing types so long
as the operands can be converted (§ 2.1.2, p. 35) to a common type.
at some of the operators defined by the library. Chapter 14 will show
Although the rules are somewhat complicated, for the most part conversions
how we can define operators for our own types. happen in unsurprising ways. For example, we can convert an integer to floating-
point, and vice versa, but we cannot convert a pointer type to floating-point. What
may be a bit surprising is that small integral type operands (e.g., bool, char,
short, etc.) are generally promoted to a larger integral type, typically int. We’ll
133 look in detail at conversions in § 4.11 (p. 159).
Overloaded Operators a reference type if the expression yields an lvalue. As an example, assume p is an
int*. Because dereference yields an lvalue, decltype(*p) is int&. On the other
The language defines what the operators mean when applied to built-in and com- hand, because the address-of operator yields an rvalue, decltype(&p) is int**,
pound types. We can also define what most operators mean when applied to class that is, a pointer to a pointer to type int.
types. Because such definitions give an alternative meaning to an existing opera-
tor symbol, we refer to them as overloaded operators. The IO library >> and <<
operators and the operators we used with strings, vectors, and iterators are all
overloaded operators. 4.1.2 Precedence and Associativity
When we use an overloaded operator, the meaning of the operator—including An expression with two or more operators is a compound expression. Evaluating
the type of its operand(s) and the result—depend on how the operator is defined. a compound expression involves grouping the operands to the operators. Prece-
However, the number of operands and the precedence and the associativity of the dence and associativity determine how the operands are grouped. That is, they
operator cannot be changed. determine which parts of the expression are the operands for each of the opera-
tors in the expression. Programmers can override these rules by parenthesizing
Lvalues and Rvalues compound expressions to force a particular grouping.
Every expression in C++ is either an rvalue (pronounced “are-value”) or an lvalue In general, the value of an expression depends on how the subexpressions are
(pronounced “ell-value”). These names are inherited from C and originally had a grouped. Operands of operators with higher precedence group more tightly than
simple mnemonic purpose: lvalues could stand on the left-hand side of an assign- operands of operators at lower precedence. Associativity determines how to group
ment whereas rvalues could not. operands with the same precedence. For example, multiplication and division
In C++, the distinction is less simple. In C++, an lvalue expression yields an have the same precedence as each other, but they have higher precedence than ad-
object or a function. However, some lvalues, such as const objects, may not be dition. Therefore, operands to multiplication and division group before operands
the left-hand operand of an assignment. Moreover, some expressions yield objects to addition and subtraction. The arithmetic operators are left associative, which
but return them as rvalues, not lvalues. Roughly speaking, when we use an object means operators at the same precdence group left to right:
as an rvalue, we use the object’s value (its contents). When we use an object as an
lvalue, we use the object’s identity (its location in memory). • Because of precedence, the expression 3+4*5 is 23, not 35.
Operators differ as to whether they require lvalue or rvalue operands and as
• Because of associativity, the expression 20-15-3 is 2, not 8.
to whether they return lvalues or rvalues. The important point is that (with one
exception that we’ll cover in § 13.6 (p. 531)) we can use an lvalue when an rvalue is As a more complicated example, a left-to-right evaluation of the following ex-
required, but we cannot use an rvalue when an lvalue (i.e., a location) is required.
pression yields 20:
When we use an lvalue in place of an rvalue, the object’s contents (its value) are
used. We have already used several operators that involve lvalues. 6 + 3 * 4 / 2 + 2
• Assignment requires a (nonconst) lvalue as its left-hand operand and yields
Other imaginable results include 9, 14, and 36. In C++, the result is 14, because this
its left-hand operand as an lvalue.
expression is equivalent to
• The address-of operator (§ 2.3.2, p. 52) requires an lvalue operand and re-
turns a pointer to its operand as an rvalue. // parentheses in this expression match default precedence and associativity
((6 + ((3 * 4) / 2)) + 2)
• The built-in dereference and subscript operators (§ 2.3.2, p. 53, and § 3.5.2,
p. 116) and the iterator dereference and string and vector subscript oper- Parentheses Override Precedence and Associativity
ators (§ 3.4.1, p. 106, § 3.2.3, p. 93, and § 3.3.3, p. 102) all yield lvalues.
We can override the normal grouping with parentheses. Parenthesized expressions
• The built-in and iterator increment and decrement operators (§ 1.4.1, p. 12, are evaluated by treating each parenthesized subexpression as a unit and other-
and § 3.4.1, p. 107) require lvalue operands and the prefix versions (which wise applying the normal precedence rules. For example, we can parenthesize the
are the ones we have used so far) also yield lvalues. expression above to force the result to be any of the four possible values:
As we present the operators, we will note whether an operand must be an lvalue
// parentheses result in alternative groupings
and whether the operator returns an lvalue.
cout << (6 + 3) * (4 / 2 + 2) << endl; // prints 36
Lvalues and rvalues also differ when used with decltype (§ 2.5.3, p. 70). cout << ((6 + 3) * 4) / 2 + 2 << endl; // prints 20
When we apply decltype to an expression (other than a variable), the result is cout << 6 + 3 * 4 / (2 + 2) << endl; // prints 9
Section 4.1 Fundamentals 137 138 Expressions
When Precedence and Associativity Matter For operators that do not specify evaluation order, it is an error for an expres-
sion to refer to and change the same object. Expressions that do so have undefined
We have already seen examples where precedence affects the correctness of our behavior (§ 2.1.2, p. 36). As a simple example, the << operator makes no guar-
programs. For example, consider the discussion in § 3.5.3 (p. 120) about derefer- antees about when or how its operands are evaluated. As a result, the following
ence and pointer arithmetic: output expression is undefined:
int ia[] = {0,2,4,6,8}; // array with five elements of type int
int last = *(ia + 4); // initializes last to 8, the value of ia[4] int i = 0;
last = *ia + 4; // last = 4, equivalent to ia[0] + 4 cout << i << " " << ++i << endl; // undefined
If we want to access the element at the location ia + 4, then the parentheses around Because this program is undefined, we cannot draw any conclusions about how
the addition are essential. Without parentheses, *ia is grouped first and 4 is added it might behave. The compiler might evaluate ++i before evaluating i, in which
to the value in *ia. case the output will be 1 1. Or the compiler might evaluate i first, in which case
The most common case that we’ve seen in which associativity matters is in the output will be 0 1. Or the compiler might do something else entirely. Because
input and output expressions. As we’ll see in § 4.8 (p. 155), the operators used this expression has undefined behavior, the program is in error, regardless of what
for IO are left associative. This associativity means we can combine several IO code the compiler generates.
operations in a single expression: There are four operators that do guarantee the order in which operands are
cin >> v1 >> v2; // read into v1 and then into v2 evaluated. We saw in § 3.2.3 (p. 94) that the logical AND (&&) operator guarantees
that its left-hand operand is evaluated first. Moreover, we are also guaranteed that
Table 4.12 (p. 166) lists all the operators organized into segments separated by the right-hand operand is evaluated only if the left-hand operand is true. The
double lines. Operators in each segment have the same precedence, and have only other operators that guarantee the order in which operands are evaluated are
higher precedence than operators in subsequent segments. For example, the prefix the logical OR (||) operator (§ 4.3, p. 141), the conditional (? :) operator (§ 4.7,
increment and dereference operators share the same precedence, which is higher p. 151), and the comma (,) operator (§ 4.10, p. 157).
than that of the arithmetic operators. The table includes a page reference to each
operator’s description. We have seen some of these operators already and will Order of Evaluation, Precedence, and Associativity
cover most of the rest in this chapter. However, there are a few operators that we
will not cover until later. Order of operand evaluation is independent of precedence and associativity. In an
expression such as f() + g() * h() + j():
E X E R C I S E S S E C T I O N 4.1.2
• Precedence guarantees that the results of g() and h() are multiplied.
Exercise 4.1: What is the value returned by 5 + 10 * 20/2?
• Associativity guarantees that the result of f() is added to the product of g()
Exercise 4.2: Using Table 4.12 (p. 166), parenthesize the following expressions to indi- and h() and that the result of that addition is added to the value of j().
cate the order in which the operands are grouped:
(a) (b)
• There are no guarantees as to the order in which these functions are called.
* vec.begin() * vec.begin() + 1
If f, g, h, and j are independent functions that do not affect the state of the same
objects or perform IO, then the order in which the functions are called is irrelevant.
If any of these functions do affect the same object, then the expression is in error
4.1.3 Order of Evaluation and has undefined behavior.
Precedence specifies how the operands are grouped. It says nothing about the E X E R C I S E S S E C T I O N 4.1.3
order in which the operands are evaluated. In most cases, the order is largely
unspecified. In the following expression
Exercise 4.3: Order of evaluation for most of the binary operators is left undefined
int i = f1() * f2(); to give the compiler opportunities for optimization. This strategy presents a trade-off
between efficient code generation and potential pitfalls in the use of the language by
we know that f1 and f2 must be called before the multiplication can be done. Af- the programmer. Do you consider that an acceptable trade-off? Why or why not?
ter all, it is their results that are multiplied. However, we have no way of knowing
whether f1 will be called before f2 or vice versa.
pointer operands. When applied to a pointer or arithmetic value, unary plus re-
A DVICE : M ANAGING C OMPOUND E XPRESSIONS
turns a (possibly promoted) copy of the value of its operand.
When you write compound expressions, two rules of thumb can be helpful: The unary minus operator returns the result of negating a (possibly promoted)
1. When in doubt, parenthesize expressions to force the grouping that the logic of copy of the value of its operand:
your program requires.
int i = 1024;
2. If you change the value of an operand, don’t use that operand elsewhere in the
int k = -i; // i is -1024
same expresion.
bool b = true;
An important exception to the second rule occurs when the subexpression that bool b2 = -b; // b2 is true!
changes the operand is itself the operand of another subexpression. For example,
in *++iter, the increment changes the value of iter. The (now changed) value of
In § 2.1.1 (p. 34) we noted that bool values should not be used for computation.
iter is the operand to the dereference operator. In this (and similar) expressions, or-
der of evaluation isn’t an issue. The increment (i.e., the subexpression that changes The result of -b is a good example of what we had in mind.
the operand) must be evaluated before the dereference can be evaluated. Such usage For most operators, operands of type bool are promoted to int. In this case,
poses no problems and is quite common. the value of b is true, which promotes to the int value 1 (§ 2.1.2, p. 35). That
(promoted) value is negated, yielding -1. The value -1 is converted back to bool
and used to initialize b2. This initializer is a nonzero value, which when converted
to bool is true. Thus, the value of b2 is true!
4.2 Arithmetic Operators
C AUTION : O VERFLOW AND O THER A RITHMETIC E XCEPTIONS
Table 4.1: Arithmetic Operators (Left Associative)
Some arithmetic expressions yield undefined results. Some of these undefined expres-
Operator Function Use sions are due to the nature of mathematics—for example, division by zero. Others are
+ unary plus + expr undefined due to the nature of computers—for example, due to overflow. Overflow
- unary minus - expr happens when a value is computed that is outside the range of values that the type
can represent.
* multiplication expr * expr Consider a machine on which shorts are 16 bits. In that case, the maximum short
/ division expr / expr is 32767. On such a machine, the following compound assignment overflows:
% remainder expr % expr
short short_value = 32767; // max value if shorts are 16 bits
+ addition expr + expr short_value += 1; // this calculation overflows
- subtraction expr - expr cout << "short_value: " << short_value << endl;
The assignment to short_value is undefined. Representing a signed value of 32768
requires 17 bits, but only 16 are available. On many systems, there is no compile-time
Table 4.1 (and the operator tables in subsequent sections) groups the opera- or run-time warning when an overflow occurs. As with any undefined behavior, what
tors by their precedence. The unary arithmetic operators have higher precedence happens is unpredictable. On our system the program completes and writes
than the multiplication and division operators, which in turn have higher prece-
short_value: -32768
dence than the binary addition and subtraction operators. Operators of higher
precedence group more tightly than do operators with lower precedence. These The value “wrapped around”: The sign bit, which had been 0, was set to 1, resulting
operators are all left associative, meaning that they group left to right when the in a negative value. On another system, the result might be different, or the program
precedence levels are the same. might behave differently, including crashing entirely.
Unless noted otherwise, the arithmetic operators may be applied to any of the
arithmetic types (§ 2.1.1, p. 32) or to any type that can be converted to an arithmetic
When applied to objects of arithmetic types, the arithmetic operators, +, -, *,
type. The operands and results of these operators are rvalues. As described in
and /, have their obvious meanings: addition, subtraction, multiplication, and
§ 4.11 (p. 159), operands of small integral types are promoted to a larger integral
division. Division between integers returns an integer. If the quotient contains a
type, and all operands may be converted to a common type as part of evaluating
fractional part, it is truncated toward zero:
these operators.
The unary plus operator and the addition and subtraction operators may also int ival1 = 21/6; // ival1 is 3; result is truncated; remainder is discarded
be applied to pointers. § 3.5.3 (p. 119) covered the use of binary + and - with int ival2 = 21/7; // ival2 is 3; no remainder; result is an integral value
Section 4.3 Logical and Relational Operators 141 142 Expressions
The % operator, known as the “remainder” or the “modulus” operator, com- Table 4.2: Logical and Relational Operators
putes the remainder that results from dividing the left-hand operand by the right-
Associativity Operator Function Use
hand operand. The operands to % must have integral type:
int ival = 42; Right ! logical NOT !expr
double dval = 3.14; Left < less than expr < expr
ival % 12; // ok: result is 6 Left <= less than or equal expr <= expr
ival % dval; // error: floating-point operand Left > greater than expr > expr
Left >= greater than or equal expr >= expr
In a division, a nonzero quotient is positive if the operands have the same sign
and negative otherwise. Earlier versions of the language permitted a negative quo- Left == equality expr == expr
tient to be rounded up or down; the new standard requires the quotient to be Left != inequality expr != expr
rounded toward zero (i.e., truncated). Left && logical AND expr && expr
The modulus operator is defined so that if m and n are integers and n is nonzero,
then (m/n)*n + m%n is equal to m. By implication, if m%n is nonzero, it has the same Left || logical OR expr || expr
sign as m. Earlier versions of the language permitted m%n to have the same sign as
n on implementations in which negative m/n was rounded away from zero, but
such implementations are now prohibited. Moreover, except for the obscure case
where -m overflows, (-m)/n and m/(-n) are always equal to -(m/n), m%(-n)
Logical AND and OR Operators
is equal to m%n, and (-m)%n is equal to -(m%n). More concretely: The overall result of the logical AND operator is true if and only if both its oper-
21 % 6; /* result is 3 */ 21 / 6; /* result is 3 */
ands evaluate to true. The logical OR (||) operator evaluates as true if either
21 % 7; /* result is 0 */ 21 / 7; /* result is 3 */ of its operands evaluates as true.
-21 % -8; /* result is -5 */ -21 / -8; /* result is 2 */ The logical AND and OR operators always evaluate their left operand before the
21 % -5; /* result is 1 */ 21 / -5; /* result is -4 */ 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:
E X E R C I S E S S E C T I O N 4.2 • The right side of an && is evaluated if and only if the left side is true.
Exercise 4.4: Parenthesize the following expression to show how it is evaluated. Test • The right side of an || is evaluated if and only if the left side is false.
your answer by compiling the expression (without parentheses) and printing its result. Several of the programs in Chapter 3 used the logical AND operator. Those
12 / 3 * 4 + 5 * 15 + 24 % 4 / 2 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:
Exercise 4.5: Determine the result of the following expressions.
index != s.size() && !isspace(s[index])
(a) -30 * 3 + 21 / 5 (b) -30 + 3 * 21 / 5
(c) 30 / 3 * 21 % 5 (d) -30 / 3 * 21 % 4 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.
Exercise 4.6: Write an expression to determine whether an int value is even or odd. As an example that uses the logical OR, imagine we have some text in a vector
Exercise 4.7: What does overflow mean? Show three expressions that will overflow. 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:
// 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
4.3 Logical and Relational Operators cout << s; // print the current element
The relational operators take operands of arithmetic or pointer type; the logical op- // blank lines and those that end with a period get a newline
if (s.empty() || s[s.size() - 1] == ’.’)
erators take operands of any type that can be converted to bool. These operators
cout << endl;
all return values of type bool. Arithmetic and pointer operand(s) with a value of
else
zero are false; all other values are true. The operands to these operators are cout << " "; // otherwise just separate with a space
rvalues and the result is an rvalue. }
After we print the current element, we check to see if we need to print a newline. In both conditions, the compiler converts val to bool. The first condition suc-
The condition in the if first checks whether s is an empty string. If so, we ceeds so long as val is nonzero; the second succeeds if val is zero.
need to print a newline regardless of the value of the right-hand operand. Only We might think we could rewrite a test of this kind as
if the string is not empty do we evaluate the second expression, which checks
if (val == true) { /* . . . */ } // true only if val is equal to 1!
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. There are two problems with this approach. First, it is longer and less direct than
It is worth noting that we declared s as a reference to const (§ 2.5.2, p. 69). the previous code (although admittedly when first learning C++ this kind of ab-
The elements in text are strings, and might be large. By making s a reference, breviation can be perplexing). Much more importantly, when val is not a bool,
we avoid copying the elements. Because we don’t need to write to the elements, this comparison does not work as expected.
we made s a reference to const. 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
Logical NOT Operator
The logical NOT operator (!) returns the inverse of the truth value of its operand. if (val == 1) { /* . . . */ }
We first used this operator in § 3.2.2 (p. 87). As another example, assuming vec is
As we’ve seen, when a bool is converted to another arithmetic type, false con-
a vector of ints, we might use the logical NOT operator to see whether vec has
verts to 0 and true converts to 1 (§ 2.1.2, p. 35). If we really cared whether val
elements by negating the value returned by empty:
was the specific value 1, we should write the condition to test that case directly.
// print the first element in vec if there is one
if (!vec.empty()) It is usually a bad idea to use the boolean literals true and false as
cout << vec[0]; operands in a comparison. These literals should be used only to compare
to an object of type bool.
The subexpression
!vec.empty()
E X E R C I S E S S E C T I O N 4.3
evaluates as true if the call to empty returns false.
Exercise 4.8: Explain when operands are evaluated in the logical AND, logical OR, and
The Relational Operators equality operators.
The relational operators (<, <=, >, <=) have their ordinary meanings and return Exercise 4.9: Explain the behavior of the condition in the following if:
bool values. These operators are left associative.
Because the relational operators return bools, the result of chaining these op- const char *cp = "Hello World";
if (cp && *cp)
erators together is likely to be surprising:
// oops! this condition compares k to the bool result of i < j Exercise 4.10: Write the condition for a while loop that would read ints from the
if (i < j < k) // true if k is greater than 1! standard input and stop when the value read is equal to 42.
This condition groups i and j to the first < operator. The bool result of that Exercise 4.11: Write an expression that tests four values, a, b, c, and d, and ensures
expression is the left-hand operand of the second less-than operator. That is, k is that a is greater than b, which is greater than c, which is greater than d.
compared to the true/false result of the first comparison! To accomplish the Exercise 4.12: Assuming i, j, and k are all ints, explain what i != j < k means.
test we intended, we can rewrite the expression as follows:
// ok: condition is true if i is smaller than j and j is smaller than k
if (i < j && j < k) { /* . . . */ }
Each of these assignments is illegal: On the other hand, the second assignment is fine. The string literal is converted
to string, and that string is assigned to s2. The result of that assignment is s2,
1024 = k; // error: literals are rvalues which has the same type as s1.
i + j = k; // error: arithmetic expressions are rvalues
ci = k; // error: ci is a const (nonmodifiable) lvalue
Assignment Has Low Precedence
The result of an assignment is its left-hand operand, which is an lvalue. The
Assignments often occur in conditions. Because assignment has relatively low
type of the result is the type of the left-hand operand. If the types of the left and
precedence, we usually must parenthesize the assignment for the condition to
right operands differ, the right-hand operand is converted to the type of the left:
work properly. To see why assignment in a condition is useful, consider the fol-
k = 0; // result: type int, value 0 lowing loop. We want to call a function until it returns a desired value—say, 42:
k = 3.14159; // result: type int, value 3
// a verbose and therefore more error-prone way to write this loop
Under the new standard, we can use a braced initializer list (§ 2.2.1, p. 43) on the int i = get_value(); // get the first value
right-hand side: while (i != 42) {
// do something . . .
k = {3.14}; // error: narrowing conversion i = get_value(); // get remaining values
vector<int> vi; // initially empty }
vi = {0,1,2,3,4,5,6,7,8,9}; // vi now has ten elements, values 0 through 9
Here we start by calling get_value followed by a loop whose condition uses the
If the left-hand operand is of a built-in type, the initializer list may contain at most value returned from that call. The last statement in this loop makes another call to
one value, and that value must not require a narrowing conversion (§ 2.2.1, p. 43). get_value, and the loop repeats. We can write this code more directly as
For class types, what happens depends on the details of the class. In the case of
vector, the vector template defines its own version of an assignment operator int i;
that can take an initializer list. This operator replaces the elements of the left-hand // a better way to write our loop---what the condition does is now clearer
side with the elements in the list on the right-hand side. while ((i = get_value()) != 42) {
Regardless of the type of the left-hand operand, the initializer list may be empty. // do something . . .
In this case, the compiler generates a value-initialized (§ 3.3.1, p. 98) temporary and }
assigns that value to the left-hand operand. The condition now more clearly expresses our intent: We want to continue until
get_value returns 42. The condition executes by assigning the result returned
Assignment Is Right Associative by get_value to i and then comparing the result of that assignment with 42.
Unlike the other binary operators, assignment is right associative: Without the parentheses, the operands to != would be the value returned from
get_value and 42. The true or false result of that test would be assigned to
int ival, jval; i—clearly not what we intended!
ival = jval = 0; // ok: each assigned 0
Because assignment has lower precedence than the relational operators,
Because assignment is right associative, the right-most assignment, jval = 0, is parentheses are usually needed around assignments in conditions.
the right-hand operand of the left-most assignment operator. Because assignment
returns its left-hand operand, the result of the right-most assignment (i.e., jval) is
assigned to ival. Beware of Confusing Equality and Assignment Operators
Each object in a multiple assignment must have the same type as its right-hand
neighbor or a type to which that neighbor can be converted (§ 4.11, p. 159): The fact that we can use assignment in a condition can have surprising effects:
if (i = j)
int ival, *pval; // ival is an int; pval is a pointer to int
ival = pval = 0; // error: cannot assign the value of a pointer to an int The condition in this if assigns the value of j to i and then tests the result of the
string s1, s2; assignment. If j is nonzero, the condition will be true. The author of this code
s1 = s2 = "OK"; // string literal "OK" converted to string almost surely intended to test whether i and j have the same value:
The first assignment is illegal because ival and pval have different types and if (i == j)
there is no conversion from the type of pval (int*) to the type of ival (int). It Bugs of this sort are notoriously difficult to find. Some, but not all, compilers are
is illegal even though zero is a value that can be assigned to either object. kind enough to warn about code such as this example.
Compound Assignment Operators mere convenience when we use these operators with iterators, because many iter-
ators do not support arithmetic.
We often apply an operator to an object and then assign the result to that same There are two forms of these operators: prefix and postfix. So far, we have used
object. As an example, consider the sum program from § 1.4.2 (p. 13): only the prefix form. This form increments (or decrements) its operand and yields
int sum = 0; the changed object as its result. The postfix operators increment (or decrement) the
// sum values from 1 through 10 inclusive operand but yield a copy of the original, unchanged value as its result:
for (int val = 1; val <= 10; ++val)
sum += val; // equivalent to sum = sum + val int i = 0, j;
j = ++i; // j = 1, i = 1: prefix yields the incremented value
This kind of operation is common not just for addition but for the other arithmetic j = i++; // j = 1, i = 2: postfix yields the unincremented value
operators and the bitwise operators, which we cover in § 4.8 (p. 152). There are
compound assignments for each of these operators: These operators require lvalue operands. The prefix operators return the object
itself as an lvalue. The postfix operators return a copy of the object’s original value
+= -= *= /= %= // arithmetic operators
<<= >>= &= ^= |= // bitwise operators; see § 4.8 (p. 152) as an rvalue.
Exercise 4.14: Explain what happens in each of the if tests: Combining Dereference and Increment in a Single Expression
if (42 = i) // ...
The postfix versions of ++ and -- are used when we want to use the current value
if (i = 42) // ...
of a variable and increment it in a single compound expression.
Exercise 4.15: The following assignment is illegal. Why? How would you correct it? As one example, we can use postfix increment to write a loop to print the values
in a vector up to but not including the first negative value:
double dval; int ival; int *pi;
dval = ival = pi = 0; auto pbeg = v.begin();
// print elements up to the first negative value
Exercise 4.16: Although the following are legal, they probably do not behave as the while (pbeg != v.end() && *beg >= 0)
programmer expects. Why? Rewrite the expressions as you think they should be. cout << *pbeg++ << endl; // print the current value and advance pbeg
(a) if (p = getPtr() != 0) (b) if (i = 1024)
The expression *pbeg++ is usually confusing to programmers new to both C++
and C. However, because this usage pattern is so common, C++ programmers must
understand such expressions.
The precedence of postfix increment is higher than that of the dereference oper-
4.5 Increment and Decrement Operators ator, so *pbeg++ is equivalent to *(pbeg++). The subexpression pbeg++ incre-
ments pbeg and yields a copy of the previous value of pbeg as its result. Accord-
The increment (++) and decrement (--) operators provide a convenient notational ingly, the operand of * is the unincremented value of pbeg. Thus, the statement
shorthand for adding or subtracting 1 from an object. This notation rises above prints the element to which pbeg originally pointed and increments pbeg.
Section 4.5 Increment and Decrement Operators 149 150 Expressions
This usage relies on the fact that postfix increment returns a copy of its original, E X E R C I S E S S E C T I O N 4.5
unincremented operand. If it returned the incremented value, we’d dereference
the incremented value, with disastrous results. We’d skip the first element. Worse, Exercise 4.17: Explain the difference between prefix and postfix increment.
if the sequence had no negative values, we would attempt to dereference one too
many elements. Exercise 4.18: What would happen if the while loop on page 148 that prints the ele-
ments from a vector used the prefix increment operator?
A DVICE : B REVITY C AN B E A V IRTUE 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
Expressions such as *pbeg++ can be bewildering—at first. However, it is a useful likely to be incorrect? Why? How might each be corrected?
and widely used idiom. Once the notation is familiar, writing (a) ptr != 0 && *ptr++ (b) ival++ && ival
cout << *iter++ << endl; (c) vec[ival++] <= vec[ival]
is easier and less error-prone than the more verbose equivalent
cout << *iter << endl;
++iter;
4.6 The Member Access Operators
It is worthwhile to study examples of such code until their meanings are immediately
clear. Most C++ programs use succinct expressions rather than more verbose equiva- The dot (§ 1.5.2, p. 23) and arrow (§ 3.4.1, p. 110) operators provide for member
lents. Therefore, C++ programmers must be comfortable with such usages. Moreover, access. The dot operator fetches a member from an object of class type; arrow is
once these expressions are familiar, you will find them less error-prone. defined so that ptr->mem is a synonym for (*ptr).mem:
// execution if left-hand side is evaluated first (a) *iter++; (b) (*iter)++; (c) *iter.empty()
*beg = toupper(*beg);
// execution if right-hand side is evaluated first (d) iter->empty(); (e) ++*iter; (f) iter++->empty();
*(beg + 1) = toupper(*beg);
or it might evaluate it in yet some other way.
4.7 The Conditional Operator another value, depending on the result of a condition. An incompletely parenthe-
sized conditional operator in an output expression can have surprising results:
The conditional operator (the ?: operator) lets us embed simple if-else logic inside
an expression. The conditional operator has the following form: cout << ((grade < 60) ? "fail" : "pass"); // prints pass or fail
cout << (grade < 60) ? "fail" : "pass"; // prints 1 or 0!
cond ? expr1 : expr2; cout << grade < 60 ? "fail" : "pass"; // error: compares cout to 60
where cond is an expression that is used as a condition and expr1 and expr2 are The second expression uses the comparison between grade and 60 as the operand
expressions of the same type (or types that can be converted to a common type). to the << operator. The value 1 or 0 is printed, depending on whether grade < 60
This operator executes by evaluating cond. If the condition is true, then expr1 is is true or false. The << operator returns cout, which is tested as the condition for
evaluated; otherwise, expr2 is evaluated. As one example, we can use a conditional the conditional operator. That is, the second expression is equivalent to
operator to determine whether a grade is pass or fail: cout << (grade < 60); // prints 1 or 0
string finalgrade = (grade < 60) ? "fail" : "pass"; cout ? "fail" : "pass"; // test cout and then yield one of the two literals
// depending on whether cout is true or false
The condition checks whether grade is less than 60. If so, the result of the expres-
The last expression is an error because it is equivalent to
sion is "fail"; otherwise the result is "pass". Like the logical AND and logical
OR (&& and ||) operators, the conditional operator guarantees that only one of cout << grade; // less-than has lower precedence than shift, so print grade first
expr1 or expr2 is evaluated. cout < 60 ? "fail" : "pass"; // then compare cout to 60!
That result of the conditional operator is an lvalue if both expressions are lval-
ues or if they convert to a common lvalue type. Otherwise the result is an rvalue.
E X E R C I S E S S E C T I O N 4.7
Nesting Conditional Operations
Exercise 4.21: Write a program to use a conditional operator to find the elements in a
We can nest one conditional operator inside another. That is, the conditional op- vector<int> that have odd value and double the value of each such element.
erator can be used as the cond or as one or both of the exprs of another conditional
Exercise 4.22: Extend the program that assigned high pass, pass, and fail grades to
expression. As an example, we’ll use a pair of nested conditionals to perform a
also assign low pass for grades between 60 and 75 inclusive. Write two versions: One
three-way test to indicate whether a grade is a high pass, an ordinary pass, or fail:
version that uses only conditional operators; the other should use one or more if
finalgrade = (grade > 90) ? "high pass" statements. Which version do you think is easier to understand and why?
: (grade < 60) ? "fail" : "pass"; Exercise 4.23: The following expression fails to compile due to operator precedence.
Using Table 4.12 (p. 166), explain why it fails. How would you fix it?
The first condition checks whether the grade is above 90. If so, the expression
after the ? is evaluated, which yields "high pass". If the condition fails, the : string s = "word";
branch is executed, which is itself another conditional expression. This conditional string pl = s + s[s.size() - 1] == ’s’ ? "" : "s" ;
asks whether the grade is less than 60. If so, the ? branch is evaluated and yields
Exercise 4.24: Our program that distinguished between high pass, pass, and fail de-
"fail". If not, the : branch returns "pass". pended on the fact that the conditional operator is right associative. Describe how that
The conditional operator is right associative, meaning (as usual) that the oper- operator would be evaluated if the operator were left associative.
ands group right to left. Associativity accounts for the fact that the right-hand
conditional—the one that compares grade to 60—forms the : branch of the left-
hand conditional expression.
Nested conditionals quickly become unreadable. It’s a good idea to nest
no more than two or three. 4.8 The Bitwise Operators
The bitwise operators take operands of integral type that they use as a collection
Using a Conditional Operator in an Output Expression of bits. These operators let us test and set individual bits. As we’ll see in § 17.2
(p. 723), we can also use these operators on a library type named bitset that
The conditional operator has fairly low precedence. When we embed a conditional represents a flexibly sized collection of bits.
expression in a larger expression, we usually must parenthesize the conditional As usual, if an operand is a “small integer,” its value is first promoted (§ 4.11.1,
subexpression. For example, we often use the conditional operator to print one or p. 160) to a larger integral type. The operand(s) can be either signed or unsigned.
Section 4.8 The Bitwise Operators 153 154 Expressions
Table 4.3: Bitwise Operators (Left Associative) bits on the left; if it is a signed type, the result is implementation defined—either
copies of the sign bit or 0-valued bits are inserted on the left.
Operator Function Use
~ bitwise NOT ~expr Bitwise NOT Operator
<< left shift expr1 << expr2 The bitwise NOT operator (the ~operator) generates a new value with the bits of
>> right shift expr1 >> expr2 its operand inverted. Each 1 bit is set to 0; each 0 bit is set to 1:
& bitwise AND expr1 & expr2
unsigned char bits = 0227; 1 0 0 1 0 1 1 1
^ bitwise XOR expr1 ^ expr2 ~bits
| bitwise OR expr1 | expr2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 0 0 0
Here, our char operand is first promoted to int. Promoting a char to int
leaves the value unchanged but adds 0 bits to the high order positions. Thus,
If the operand is signed and its value is negative, then the way that the “sign bit” promoting bits to int adds 24 high order bits, all of which are 0-valued. The bits
is handled in a number of the bitwise operations is machine dependent. Moreover, in the promoted value are inverted.
doing a left shift that changes the value of the sign bit is undefined.
Bitwise AND, OR, and XOR Operators
Because there are no guarantees for how the sign bit is handled, we
The AND (&), OR (|), and XOR (^) operators generate new values with the bit pat-
strongly recommend using unsigned types with the bitwise operators.
tern composed from its two operands:
We define quiz1 as an unsigned long. Thus, quiz1 will have at least 32 bits on cout << "hi" << " there" << endl;
any machine. We explicitly initialize quiz1 to ensure that the bits start out with
well-defined values. executes as
The teacher must be able to set and test individual bits. For example, we’d ( (cout << "hi") << " there" ) << endl;
like to be able to set the bit corresponding to student number 27 to indicate that
this student passed the quiz. We can indicate that student number 27 passed by In this statement, the operand "hi" is grouped with the first << symbol. Its result
creating a value that has only bit 27 turned on. If we then bitwise OR that value is grouped with the second, and then that result is grouped with the third.
with quiz1, all the bits except bit 27 will remain unchanged. The shift operators have midlevel precedence: lower than the arithmetic oper-
For the purpose of this example, we will count the bits of quiz1 by assigning ators but higher than the relational, assignment, and conditional operators. These
0 to the low-order bit, 1 to the next bit, and so on. relative precedence levels mean we usually have to use parentheses to force the
We can obtain a value indicating that student 27 passed by using the left-shift correct grouping of operators with lower precedence.
operator and an unsigned long integer literal 1 (§ 2.1.3, p. 38):
cout << 42 + 10; // ok: + has higher precedence, so the sum is printed
1UL << 27 // generate a value with only bit number 27 set cout << (10 < 42); // ok: parentheses force intended grouping; prints 1
cout << 10 < 42; // error: attempt to compare cout to 42!
1UL has a 1 in the low-order bit and (at least) 31 zero bits. We specified unsigned
long because ints are only guaranteed to have 16 bits, and we need at least 27. The last cout is interpreted as
This expression shifts the 1 bit left 27 positions inserting 0 bits behind it.
Next we OR this value with quiz1. Because we want to update the value of (cout << 10) < 42;
quiz1, we use a compound assignment (§ 4.4, p. 147): which says to “write 10 onto cout and then compare the result of that operation
quiz1 |= 1UL << 27; // indicate student number 27 passed (i.e., cout) to 42.”
The |= operator executes analogously to how += does. It is equivalent to
E X E R C I S E S S E C T I O N 4.8
quiz1 = quiz1 | 1UL << 27; // equivalent to quiz1 |= 1UL << 27;
Imagine that the teacher reexamined the quiz and discovered that student 27 Exercise 4.25: What is the value of ~’q’ << 6 on a machine with 32-bit ints and 8 bit
actually had failed the test. The teacher must now turn off bit 27. This time we chars, that uses Latin-1 character set in which ’q’ has the bit pattern 01110001?
need an integer that has bit 27 turned off and all the other bits turned on. We’ll Exercise 4.26: In our grading example in this section, what would happen if we used
bitwise AND this value with quiz1 to turn off just that bit: unsigned int as the type for quiz1?
quiz1 &= ~(1UL << 27); // student number 27 failed Exercise 4.27: What is the result of each of these expressions?
We obtain a value with all but bit 27 turned on by inverting our previous value. unsigned long ul1 = 3, ul2 = 7;
That value had 0 bits in all but bit 27, which was a 1. Applying the bitwise NOT to (a) ul1 & ul2 (b) ul1 | ul2
that value will turn off bit 27 and turn on all the others. When we bitwise AND this (c) ul1 && ul2 (d) ul1 || ul2
value with quiz1, all except bit 27 will remain unchanged.
Finally, we might want to know how the student at position 27 fared:
bool status = quiz1 & (1UL << 27); // how did student number 27 do?
Here we AND a value that has bit 27 turned on with quiz1. The result is nonzero 4.9 The sizeof Operator
(i.e., true) if bit 27 of quiz1 is also on; otherwise, it evaluates to zero.
The sizeof operator returns the size, in bytes, of an expression or a type name.
Shift Operators (aka IO Operators) Are Left Associative The operator is right associative. The result of sizeof is a constant expression
(§ 2.4.4, p. 65) of type size_t (§ 3.5.2, p. 116). The operator takes one of two
Although many programmers never use the bitwise operators directly, most pro- forms:
grammers do use overloaded versions of these operators for IO. An overloaded
operator has the same precedence and associativity as the built-in version of that sizeof (type)
operator. Therefore, programmers need to understand the precedence and associa- sizeof expr
tivity of the shift operators even if they never use them with their built-in meaning. In the second form, sizeof returns the size of the type returned by the given ex-
Because the shift operators are left associative, the expression pression. The sizeof operator is unusual in that it does not evaluate its operand:
Section 4.10 Comma Operator 157 158 Expressions
• sizeof an array is the size of the entire array. It is equivalent to taking the This loop increments ix and decrements cnt in the expression in the for header.
sizeof the element type times the number of elements in the array. Note Both ix and cnt are changed on each trip through the loop. As long as the test of
that sizeof does not convert the array to a pointer. ix succeeds, we reset the current element to the current value of cnt.
• sizeof a string or a vector returns only the size of the fixed part of these
types; it does not return the size used by the object’s elements. E X E R C I S E S S E C T I O N 4.10
Because sizeof returns the size of the entire array, we can determine the num-
Exercise 4.31: The program in this section used the prefix increment and decrement
ber of elements in an array by dividing the array size by the element size: operators. Explain why we used prefix and not postfix. What changes would have to
// sizeof(ia)/sizeof(*ia) returns the number of elements in ia be made to use the postfix versions? Rewrite the program using postfix operators.
constexpr size_t sz = sizeof(ia)/sizeof(*ia);
Exercise 4.32: Explain the following loop.
int arr2[sz]; // ok sizeof returns a constant expression § 2.4.4 (p. 65)
constexpr int size = 5;
Because sizeof returns a constant expression, we can use the result of a sizeof int ia[size] = {1,2,3,4,5};
expression to specify the dimension of an array. for (int *ptr = ia, ix = 0;
ix != size && ptr != ia+size;
++ix, ++ptr) { / * . . . */ }
4.10 Comma Operator Exercise 4.33: Using Table 4.12 (p. 166) explain what the following expression does:
The comma operator takes two operands, which it evaluates from left to right. Like someValue ? ++x, ++y : --x, --y
the logical AND and logical OR and the conditional operator, the comma operator
guarantees the order in which its operands are evaluated.
cval + lval; // cval converted to long type can be converted to a const void*. We’ll see in § 15.2.2 (p. 597) that there is
ival + ulval; // ival converted to unsigned long an additional pointer conversion that applies to types related by inheritance.
usval + ival; // promotion depends on the size of unsigned short and int Conversions to bool: There is an automatic conversion from arithmetic or pointer
uival + lval; // conversion depends on the size of unsigned int and long
types to bool. If the pointer or arithmetic value is zero, the conversion yields
In the first addition, the character constant lowercase ’a’ has type char, which false; any other value yields true:
is a numeric value (§ 2.1.1, p. 32). What that value is depends on the machine’s char *cp = get_string();
character set. On our machine, ’a’ has the numeric value 97. When we add ’a’ if (cp) /* . . . */ // true if the pointer cp is not zero
to a long double, the char value is promoted to int, and then that int value while (*cp) /* . . . */ // true if *cp is not the null character
is converted to a long double. The converted value is added to the literal. The Conversion to const: We can convert a pointer to a nonconst type to a pointer to
other interesting cases are the last two expressions involving unsigned values. The the corresponding const type, and similarly for references. That is, if T is a type,
type of the result in these expressions is machine dependent. we can convert a pointer or a reference to T into a pointer or reference to const T,
respectively (§ 2.4.1, p. 61, and § 2.4.2, p. 62):
E X E R C I S E S S E C T I O N 4.11.1 int i;
const int &j = i; // convert a nonconst to a reference to const int
Exercise 4.34: Given the variable definitions in this section, explain what conversions const int *p = &i; // convert address of a nonconst to the address of a const
take place in the following expressions: int &r = j, *q = p; // error: conversion from const to nonconst not allowed
(a) if (fval) (b) dval = fval + ival; (c) dval + ival * cval; The reverse conversion—removing a low-level const—does not exist.
Remember that you may need to consider the associativity of the operators. Conversions Defined by Class Types: Class types can define conversions that the
Exercise 4.35: Given the following definitions, compiler will apply automatically. The compiler will apply only one class-type
conversion at a time. In § 7.5.4 (p. 295) we’ll see an example of when multiple
char cval; int ival; unsigned int ui;
conversions might be required, and will be rejected.
float fval; double dval;
Our programs have already used class-type conversions: We use a class-type
identify the implicit type conversions, if any, taking place: conversion when we use a C-style character string where a library string is ex-
(a) cval = ’a’ + 3; (b) fval = ui - ival * 1.0; pected (§ 3.5.5, p. 124) and when we read from an istream in a condition:
(c) dval = ui * fval; (d) cval = ival + fval + dval; string s, t = "a value"; // character string literal converted to type string
while (cin >> s) // while condition converts cin to bool
The condition (cin >> s) reads cin and yields cin as its result. Conditions ex-
pect a value of type bool, but this condition tests a value of type istream. The
4.11.2 Other Implicit Conversions IO library defines a conversion from istream to bool. That conversion is used
(automatically) to convert cin to bool. The resulting bool value depends on the
In addition to the arithmetic conversions, there are several additional kinds of im- state of the stream. If the last read succeeded, then the conversion yields true. If
plicit conversions. These include: the last attempt failed, then the conversion to bool yields false.
Array to Pointer Conversions: In most expressions, when we use an array, the
array is automatically converted to a pointer to the first element in that array:
int ia[10]; // array of ten ints
4.11.3 Explicit Conversions
int* ip = ia; // convert ia to a pointer to the first element Sometimes we want to explicitly force an object to be converted to a different type.
This conversion is not performed when an array is used with decltype or as the For example, we might want to use floating-point division in the following code:
operand of the address-of (&), sizeof, or typeid (which we’ll cover in § 19.2.2 int i, j;
(p. 826)) operators. The conversion is also omitted when we initialize a reference to double slope = i/j;
an array (§ 3.5.1, p. 114). As we’ll see in § 6.7 (p. 247), a similar pointer conversion
happens when we use a function type in an expression. To do so, we’d need a way to explicitly convert i and/or j to double. We use a
cast to request an explicit conversion.
Pointer Conversions: There are several other pointer conversions: A constant in-
tegral value of 0 and the literal nullptr can be converted to any pointer type; a Although necessary at times, casts are inherently dangerous constructs.
pointer to any nonconst type can be converted to void*, and a pointer to any
Named Casts Only a const_cast may be used to change the constness of an expression.
Trying to change whether an expression is const with any of the other forms of
A named cast has the following form: named cast is a compile-time error. Similarly, we cannot use a const_cast to
cast-name<type>(expression); change the type of an expression:
where type is the target type of the conversion, and expression is the value to be const char *cp;
cast. If type is a reference, then the result is an lvalue. The cast-name may be one of // error: static_cast can’t cast away const
static_cast, dynamic_cast, const_cast, and reinterpret_cast. We’ll char *q = static_cast<char*>(cp);
cover dynamic_cast, which supports the run-time type identification, in § 19.2 static_cast<string>(cp); // ok: converts string literal to string
(p. 825). The cast-name determines what kind of conversion is performed. const_cast<string>(cp); // error: const_cast only changes constness
Conventionally we say that a cast that converts a const object to a nonconst Old-Style Casts
type “casts away the const.” Once we have cast away the const of an object, the
compiler will no longer prevent us from writing to that object. If the object was In early versions of C++, an explicit cast took one of the following two forms:
originally not a const, using a cast to obtain write access is legal. However, using type (expr); // function-style cast notation
a const_cast in order to write to a const object is undefined. (type) expr; // C-language-style cast notation
Section 4.11 Type Conversions 165 166 Expressions
integral promotions conversions that take result Value or object obtained by evaluat- ˆ operator Bitwise exclusive or operator. returns a copy of the original, unchanged
a smaller integral type to its most closely ing an expression. Generates a new integral value in which value of the operand. Note: Iterators have
related larger integral type. Operands of each bit position is 1 if either but not both -- even if they do not have the -.
small integral types (e.g., short, char, rvalue Expression that yields a value but operands contain a 1 in that bit position;
etc.) are always promoted, even in contexts not the associated location, if any, of that otherwise, the bit is 0. << operator The left-shift operator. Shifts
where such conversions might not seem to value. bits in a (possibly promoted) copy of the
|| operator Logical OR operator. Yields value of the left-hand operand to the left.
be required.
short-circuit evaluation Term used to de- true if either operand is true. The right- Shifts as many bits as indicated by the right-
lvalue An expression that yields an object scribe how the logical AND and logical OR hand operand is evaluated only if the left- hand operand. The right-hand operand
or function. A nonconst lvalue that de- operators execute. If the first operand to hand operand is false. must be zero or positive and strictly less
notes an object may be the left-hand oper- these operators is sufficient to determine than the number of bits in the result. Left-
the overall result, evaluation stops. We are | operator Bitwise OR operator. Generates
and of assignment. hand operand should be unsigned; if the
guaranteed that the second operand is not a new integral value in which each bit po-
sition is 1 if either operand has a 1 in that left-hand operand is signed, it is unde-
operands Values on which an expression evaluated. fined if a shift causes a different bit to shift
position; otherwise the bit is 0.
operates. Each operator has one or more into the sign bit.
operands associated with it. sizeof Operator that returns the size, in
++ operator The increment operator. The
bytes, to store an object of a given type >> operator The right-shift operator. Like
increment operator has two forms, prefix
operator Symbol that determines what ac- name or of the type of a given expression. the left-shift operator except that bits are
and postfix. Prefix increment yields an
tion an expression performs. The language lvalue. It adds 1 to the operand and returns shifted to the right. If the left-hand oper-
defines a set of operators and what those static_cast An explicit request for a well-
the changed value of the operand. Postfix and is signed, it is implementation defined
operators mean when applied to values of defined type conversion. Often used to
increment yields an rvalue. It adds 1 to the whether bits shifted into the result are 0 or
built-in type. The language also defines the override an implicit conversion that the
operand and returns a copy of the original, a copy of the sign bit.
precedence and associativity of each opera- compiler would otherwise perform.
unchanged value of the operand. Note: It-
tor and specifies how many operands each erators have ++ even if they do not have the
~ operator Bitwise NOT operator. Gener-
unary operators Operators that take a sin-
operator takes. Operators may be over- ates a new integral value in which each bit
gle operand. + operator.
loaded and applied to values of class type. is an inverted copy of the corresponding bit
, operator Comma operator. Binary oper- -- operator The decrement operator has in the (possibly promoted) operand.
order of evaluation Order, if any, in which ator that is evaluated left to right. The result two forms, prefix and postfix. Prefix decre-
the operands to an operator are evaluated. of a comma expression is the value of the ment yields an lvalue. It subtracts 1 from ! operator Logical NOT operator. Returns
In most cases, the compiler is free to eval- the operand and returns the changed value the inverse of the bool value of its operand.
right-hand operand. The result is an lvalue
uate operands in any order. However, the if and only if that operand is an lvalue. of the operand. Postfix decrement yields an Result is true if operand is false and vice
operands are always evaluated before the rvalue. It subtracts 1 from the operand and versa.
operator itself is evaluated. Only the &&, ||, ?: operator Conditional operator. Pro-
?:, and comma operators specify the order vides an if-then-else expression of the form
in which their operands are evaluated.
cond ? expr1 : expr2;
overloaded operator Version of an opera-
tor that is defined for use with a class type. If the condition cond is true, then expr1 is
We’ll see in Chapter 14 how to define over- evaluated. Otherwise, expr2 is evaluated.
loaded versions of operators. The type expr1 and expr2 must be the same
type or be convertible to a common type.
precedence Defines the order in which Only one of expr1 or expr2 is evaluated.
different operators in a compound expres-
sion are grouped. Operators with higher && operator Logical AND operator. Result
precedence are grouped more tightly than is true if both operands are true. The
operators with lower precedence. right-hand operand is evaluated only if the
left-hand operand is true.
promoted See integral promotions.
& operator Bitwise AND operator. Gener-
reinterpret_cast Interprets the contents of ates a new integral value in which each bit
the operand as a different type. Inherently position is 1 if both operands have a 1 in that
machine dependent and dangerous. position; otherwise the bit is 0.