5.10. Evaluating Compound Expressions5.10. 复合表达式的求值An expression with two or more operators is a compound expression. In a compound expression, the way in which the operands are grouped to the operators may determine the result of the overall expression. If the operands group in one way, the result differs from what it would be if they grouped another way. 含有两个或更多操作符的表达式称为复合表达式。在复合表达式中,操作数和操作符的结合方式决定了整个表达式的值。表达式的结果会因为操作符和操作数的分组结合方式的不同而不同。 Precedence and associativity determine how the operands are grouped. That is, precedence and associativity determine which part of the expression is the operand for each of the operators in the expression. Programmers can override these rules by parenthesizing compound expressions to force a particular grouping. 操作数的分组结合方式取决于操作符的优先级和结合性。也就是说,优先级和结合性决定了表达式的哪个部分用作哪个操作符的操作数。如果程序员不想考虑这些规则,可以在复合表达式中使用圆括号强制实现某个特殊的分组。
5.10.1. Precedence5.10.1. 优先级The value of an expression depends on how the subexpressions are grouped. For example, in the following expression, a purely left-to-right evaluation yields 20: 表达式的值取决于其子表达式如何分组。例如,下面的表达式,如果纯粹从左向右计算,结果为 20: 6 + 3 * 4 / 2 + 2; Other imaginable results include 9, 14, and 36. In C++, the result is 14. 想像中其他可能的结果包括 9、14 和 36。在 C++ 中,该表达式的值应为 14。 Multiplication and division have higher precedence than addition. Their operands are bound to the operator in preference to the operands to addition. Multiplication and division have the same precedence as each other. Operators also have associativity, which determines how operators at the same precedence level are grouped. The arithmetic operators are left associative, which means they group left to right. We now can see that our expression is equivalent to 乘法和除法的优先级高于加法操作,于是它们的操作数先于加法操作的操作数计算。但乘法和除法的优先级相同。当操作符的优先级相同时,由其结合性决定求解次序。算术操作具有左结合性,这意味着它们从左向右结合。因此上面表达式等效于: int temp = 3 * 4; // 12 int temp2 = temp / 2; // 6 int temp3 = temp2 + 6; // 12 int result = temp3 + 2; // 14 Parentheses Override Precedence圆括号凌驾于优先级之上We can override precedence with parentheses. Parenthesized expressions are evaluated by treating each parenthesized subexpression as a unit and otherwise applying the normal precedence rules. For example, we can use parentheses on our initial expression to force the evaluation to result in any of the four possible values: 我们可使用圆括号推翻优先级的限制。使用圆括号的表达式将用圆括号括起来的子表达式视为独立单元先计算,其他部分则以普通的优先级规则处理。例如,下面的程序在前述表达式上添加圆括号,强行更改其操作次序,可能得到四种结果: // parentheses on this expression match default precedence/associativity cout << ((6 + ((3 * 4) / 2)) + 2) << endl; // prints 14 // parentheses result in alternative groupings cout << (6 + 3) * (4 / 2 + 2) << endl; // prints 36 cout << ((6 + 3) * 4) / 2 + 2 << endl; // prints 20 cout << 6 + 3 * 4 / (2 + 2) << endl; // prints 9 We have already seen examples where precedence rules affect the correctness of our programs. For example, consider the expression described in the "Advice" box on page 164: 我们已经通过前面的例子了解了优先级规则如何影响程序的正确性。例如,考虑第 5.5 节第二个建议框中描述的表达式: *iter++; Precedence says that ++ has higher precedence than *. That means that iter++ is grouped first. The operand of *, therefore, is the result of applying the increment operator to iter. If we wanted to increment the value that iter denotes, we'd have to use parentheses to force our intention: 其中,++ 的优先级高于*操作符,这就意味着 iter++ 先结合。而操作符 * 的操作数是 iter 做了自增操作后的结果。如果我们希望对 iter 所指向的值做自增操作,则必须使用圆括号强制实现我们的目的: (*iter)++; // increment value to which iter refers and yield unincremented value The parentheses specify that the operand of * is iter. The expression now uses *iter as the operand to ++. 圆括号指明操作符 * 的操作数是 iter,然后表达式以 *iter 作为 ++操作符的操作数。 As another example, recall the condition in the while on page 161: 另一个例子,回顾一下第 5.4.2 节中的 while 循环条件: while ((i = get_value()) != 42) { The parentheses around the assignment were necessary to implement the desired operation, which was to assign to i the value returned from get_value and then test that value to see whether it was 42. Had we failed to parenthesize the assignment, the effect would be to test the return value to see whether it was 42. The true or false value of that test would then be assigned to i, meaning that i would either be 1 or 0. 赋值操作上的圆括号是必需的,这样才能实现预期的操作:将 get_value 的返回值赋给 i,然后检查刚才赋值的结果是否为42。如果赋值操作上没有加圆括号,结果将是先判断 get_value 的返回值是否为 42,然后将判断结果 true 或 false 值赋给 i,这意味着 i 的值只能是 1 或 0。 5.10.2. Associativity5.10.2. 结合性Associativity specifies how to group operators at the same precedence level. We have also seen cases where associativity matters. As one example, the assignment operator is right associative. This fact allows concatenated assignments: 结合性规定了具有相同优先级的操作符如何分组。我们已经遇到过涉及结合性的例子。其中之一使用了赋值操作的右结合性,这个特性允许将多个赋值操作串接起来: ival = jval = kval = lval // right associative (ival = (jval = (kval = lval))) // equivalent, parenthesized version This expression first assigns lval to kval, then the result of that to jval, and finally the result of that to ival. 该表达式首先将 lval 赋给 kval ,然后将 kval 的值赋给 jval ,最后将 jval 的值再赋给 ival。 The arithmetic operators, on the other hand, are left associative. The expression 另一方面,算术操作符为左结合。表达式 ival * jval / kval * lval // left associative (((ival * jval) / kval) * lval) // equivalent, parenthesized version multiplies ival and jval, then divides that result by kval, and finally multiplies the result of the division by lval. 先对 ival 和 jval 做乘法操作,然后乘积除以 kval,最后再将其商与 lval 相乘。 Table 5.4 presents the full set of operators ordered by precedence. The table is organized into segments separated by double lines. Operators in each segment have the same precedence, and have higher precedence than operators in sub-sequent segments. For example, the prefix increment and dereference operators share the same precedence and have higher precedence than the arithmetic or relational operators. We have seen most of these operators, although a few will not be defined until later chapters. 表 5.4 按照优先级顺序列出了 C++ 的全部操作符。该表以双横线分割成不同的段,每段内各个操作符的优先级相同,且都高于后面各段中的操作符。例如,前自增操作符和解引用操作符的优先级相同,它们的优先级都比算术操作符或关系操作符高。此表中大部分操作符已经介绍过,而少数未介绍的操作符将在后续章节中学习。 Table 5.4. Operator Precedence表 5.4. 操作符的优先级
5.10.3. Order of Evaluation5.10.3. 求值顺序In Section 5.2 (p. 152) we saw that the && and || operators specify the order in which their operands are evaluated: In both cases the right-hand operand is evaluated if and only if doing so might affect the truth value of the overall expression. Because we can rely on this property, we can write code such as 在第 5.2 节中,我们讨论了 && 和 || 操作符计算其操作数的次序:当且仅当其右操作数确实影响了整个表达式的值时,才计算这两个操作符的右操作数。根据这个原则,可编写如下代码: // iter only dereferenced if it isn't at end while (iter != vec.end() && *iter != some_val) The only other operators that guarantee the order in which operands are evaluated are the conditional (?:) and comma operators. In all other cases, the order is unspecified. C++中,规定了操作数计算顺序的操作符还有条件(?:)和逗号操作符。除此之外,其他操作符并未指定其操作数的求值顺序。 For example, in the expression 例如,表达式 f1() * f2(); we know that both f1 and f2 must be called before the multiplication can be done. After all, their results are what is multiplied. However, we have no way to know whether f1 will be called before f2 or vice versa. 在做乘法操作之前,必须调用 f1 函数和 f2 函数,毕竟其调用结果要相乘。然而,我们却无法知道到底是先调用 f1 还是先调用 f2。
The order of operand evaluation matters if one subexpression changes the value of an operand used in another subexpression: 如果一个子表达式修改了另一个子表达式的操作数,则操作数的求解次序就变得相当重要:
// oops! language does not define order of evaluation
if (ia[index++] < ia[index])
The behavior of this expression is undefined. The problem is that the left- and right-hand operands to the < both use the variable index. However, the left-hand operand involves changing the value of that variable. Assuming index is zero, the compiler might evaluate this expression in one of the following two ways: 此表达式的行为没有明确定义。问题在于:< 操作符的左右操作数都使用了 index 变量,但是,左操作数更改了该变量的值。假设 index 初值为 0,编译器可以用下面两种方式之一求该表达式的值: if (ia[0] < ia[0]) // execution if rhs is evaluated first if (ia[0] < ia[1]) // execution if lhs is evaluated first We can guess that the programmer intended that the left operand be evaluated, thereby incrementing index. If so, the comparison would be between ia[0] and ia[1]. The language, however, does not guarantee a left-to-right evaluation order. In fact, an expression like this is undefined. An implementation might evaluate the right-hand operand first, in which case ia[0] is compared to itself. Or the implementation might do something else entirely. 可以假设程序员希望先求左操作数的值,因此 index 的值加 1。如果是这样的话,比较 ia[0] 和 ia[1] 的值。然而,C++ 语言不能确保从左到右的计算次序。事实上,这类表达式的行为没有明确定义。一种实现可能是先计算右操作数,于是 ia[0] 与自己做比较,要不然就是做完全不同的操作。
One safe and machine-independent way to rewrite the previous comparison of two array elements is 以一种安全而且独立于机器的方式重写上述比较两个数组元素的程序:
if (ia[index] < ia[index + 1]) {
// do whatever
}
++index;
Now neither operand can affect the value of the other. 现在,两个操作数的值不会相互影响。 |