C++ Primer 第四章 表达式

4.1. Fundamentals

4.1.1. Lvalues and Rvalues

  • Every expression in C++ is either an rvalue or an lvalue.

  • In C++, an lvalue expression yields an object or a function.

  • Roughly speaking, when we use an object 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).

  • Operators differ as to whether they require lvalue or rvalue operands and as to whether they return lvalues or rvalues.

    • Assignment requires a (nonconst) lvalue as its left-hand operand and yields its left-hand operand as an lvalue.
    • The address-of operator requires an lvalue operand and returns a pointer to its operand as an rvalue.
    • The built-in dereference and subscript operators and the iterator dereference and string and vector subscript operators all yield lvalues.
    • The built-in and iterator increment and decrement operators require lvalue operands and the prefix versions also yield lvalues.
  • Lvalues and rvalues also differ when used with decltype.

    • When we apply decltype to an expression (other than a variable), the result is 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 hand, because the address-of operator yields an rvalue, decltype(&p) is int**, that is, a pointer to a pointer to type int.

4.1.2. Precedence and Associativity

int i = f1() * f2();

We have no way of knowing whether f1 will be called before f2 or vice versa.

int i = 0;
cout << i << " " << ++i << endl; // undefined

The compiler might evaluate ++i before evaluating i, in which case the output will be 1 1.
Or the compiler might evaluate i first, in which case the output will be 0 1.
Or the compiler might do something else entirely.


  • The logical AND (&&) operator guarantees that its left-hand operand is evaluated first.

  • Moreover, we are also guaranteed that the right-hand operand is evaluated only if the left-hand operand is true.

  • The only other operators that guarantee the order in which operands are evaluated are the logical OR (||) operator, the conditional (? 😃 operator, and the comma (,) operator

4.2. Arithmetic Operators

In a division, the new standard requires the quotient to be rounded toward zero (i.e., truncated).

21 % 6; /* result is 3 */ 			21 / 6; /* result is 3 */
21 % 7; /* result is 0 */		 	21 / 7; /* result is 3 */
-21 % -8; /* result is -5 */ 		-21 / -8; /* result is 2 */
21 % -5; /* result is 1 */ 			21 / -5; /* result is -4 */

4.4. Assignment Operators

int i = 0, j = 0, k = 0; // initializations, not assignment
const int ci = i; // initialization, not assignment

1024 = k; // error: literals are rvalues
i + j = k; // error: arithmetic expressions are rvalues
ci = k; // error: ci is a const (nonmodifiable) lvalue

k = 0; // result: type int, value 0
k = 3.14159; // result: type int, value 3
  • The left-hand operand of an assignment operator must be a modifiable lvalue. For example, given

  • The result of an assignment is its left-hand operand, which is an lvalue.

  • The type of the result is the type of the left-hand operand.

  • If the types of the left and right operands differ, the right-hand operand is converted to the type of the left:

  • Under the new standard, we can use a braced initializer list on the right-hand side:

k = {3.14}; // error: narrowing conversion
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
  • If the left-hand operand is of a built-in type, the initializer list may contain at most one value, and that value must not require a narrowing conversion.

4.5. Increment and Decrement Operators

  • The prefix increments (or decrements) its operand and yields the changed object as its result.

  • The postfix operators increment (or decrement) the operand but yield a copy of the original, unchanged value as its result.

  • The postfix operator must store the original value so that it can return the unincremented value as its result.

// the behavior of the following loop is undefined!
while (beg != s.end() && !isspace(*beg))
	*beg = toupper(*beg++); // error: this assignment is undefined

The problem is that 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

4.9. The sizeof Operator

C++ sizeof 运算符

  • The sizeof operator returns the size, in bytes, of an expression or a type name.

  • The operator is right associative.

  • The result of sizeof is a constant expression of type size_t.

sizeof (type)
sizeof expr

sizeof an array is the size of the entire array. It is equivalent to taking the sizeof the element type times the number of elements in the array.

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.

4.10. Comma Operator

  • The comma operator evaluates from left to right.
  • The comma operator guarantees the order in which its operands are evaluated.
  • The result of a comma expression is the value of its right-hand expression.
  • The result is an lvalue if the right-hand operand is an lvalue.

4.11. Type Conversions

int ival = 3.541 + 3; // the compiler might warn about loss of precision
  1. 3 is converted to double, floating-point addition is done, and the result is a double.

  2. The double result of the addition is converted to int and used to initialize ival.

4.11.1. The Arithmetic Conversions

The rules define a hierarchy of type conversions in which operands to an operator are converted to the widest type.

4.11.2. Other Implicit Conversions

Array to Pointer Conversions

int ia[10]; // array of ten ints
int* ip = ia; // convert ia to a pointer to the first element

This conversion is not performed when an array is used with decltype or as the operand of the address-of (&), sizeof, or typeid operators.

The conversion is also omitted when we initialize a reference to an array.

Pointer Conversions

  • A constant integral value of 0 and the literal nullptr can be converted to any pointer type.

  • A pointer to any nonconst type can be converted to void*.

  • A pointer to any type can be converted to a const void*.

Conversion to const

int i;
const int &j = i; // convert a nonconst to a reference to const int
const int *p = &i; // convert address of a nonconst to the address of a const
int &r = j, *q = p; // error: conversion from const to nonconst not allowed

Conversions Defined by Class Types

string s, t = "a value"; // character string literal converted to type string
while (cin >> s) // while condition converts cin to bool

If the last read succeeded, then the conversion yields true. If the last attempt failed, then the conversion to bool yields false.

4.11.3. Explicit Conversions

Named Casts

cast-name<type>(expression);

Where type is the target type of the conversion, and expression is the value to be cast.

If type is a reference, then the result is an lvalue.

The cast-name may be one of static_cast, dynamic_cast, const_cast, and reinterpret_cast.

static_cast
// cast used to force floating-point division
double slope = static_cast<double>(j) / i;
  • Any well-defined type conversion, other than those involving low-level const, can be requested using a static_cast.

  • A static_cast is often useful when a larger arithmetic type is assigned to a smaller type.

  • A static_cast is also useful to perform a conversion that the compiler will not generate automatically.

void* p = &d; // ok: address of any nonconst object can be stored in a void*
// ok: converts void* back to the original pointer type
double *dp = static_cast<double*>(p);

When we store a pointer in a void* and then use a static_cast to cast the pointer back to its original type, we are guaranteed that the pointer value is preserved.

The result of the cast will be equal to the original address value.

However, we must be certain that the type to which we cast the pointer is the actual type of that pointer; if the types do not match, the result is undefined.

const_cast

A const_cast changes only a low-level const in its operand.

const char *pc;
char *p = const_cast<char*>(pc); 
// ok: but writing through p is undefined ???

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 originally not a const, using a cast to obtain write access is legal.

However, using a const_cast in order to write to a const object is undefined. (?)

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 named cast is a compile-time error.

Similarly, we cannot use a const_cast to change the type of an expression:

const char *cp;
// error: static_cast can't cast away const
char *q = static_cast<char*>(cp);
static_cast<string>(cp); // ok: converts string literal to string
const_cast<string>(cp); // error: const_cast only changes constness
reinterpret_cast

reinterpret_cast in C++

A reinterpret_cast generally performs a low-level reinterpretation of the bit pattern of its operands.

int *ip;
char *pc = reinterpret_cast<char*>(ip);

Old-Style Casts

type (expr); // function-style cast notation
(type) expr; // C-language-style cast notation

4.12. Operator Precedence Table

1234

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值