3.5 Expression Rules

C++ has the usual unary operators such as logical negation (!a), binary operators such as addition (a+b), and even a ternary operator (a?b:c). Unlike many other languages, an array subscript is also an operator (a[b]), and a function call is an n-ary operator (e.g., a(b, c, d)).

Every operator has a precedence. Operators with higher precedence are grouped so that they are logically evaluated before operators with lower precedence. (Note that precedence determines how the compiler parses the expression, not necessarily the actual order of computation. For example, in the expression a( ) + b( ) * c( ), the multiplication has higher precedence, but a( ) might be called first.)

Some operators group from left to right. For example, the expression x / y / z is equivalent to (x / y) / z. Other operators group right to left, as in x = y = z, which is equivalent to x = (y = z). The order of grouping is called the operator's associativity.

When reading C++ expressions, you must be aware of the precedence and associativity of the operators involved. For example, *ptr++ is read as *(ptr++) because the postfix ++ operator has higher precedence than the unary * operator.

Table 3-1 summarizes the syntax, precedence, and associativity of each kind of expression. The subsections that follow describe the kinds of expressions in depth; each subsection covers a single precedence group.

Table 3-1. Expression syntax and associativity, grouped by precedence

Group

Associativity

Expression

Primary (highest precedence)

Left-to-right

literal

this(

expr)

name

::name

class-or-namespace

:: name

Postfix

Left-to-right

pointer [expr]

expr

( expr, ...)

type( expr, ...)

object.member

pointer ->member

cast_keyword <type >(expr )

typeid(expr )

typeid(type)

lvalue++

lvalue--

Unary

Right-to-left

++lvalue

--lvalue

~expr

compl expr

! expr

not expr

+ expr

- expr

* pointer

& lvalue

sizeof expr

sizeof( type)

new-expr

delete-expr

Cast

Right-to-left

( type ) expr

Pointer to Member

Left-to-right

object .* expr

pointer ->* expr

Multiplicative

Left-to-right

expr *expr

expr / expr

expr % expr

Additive

Left-to-right

expr + expr

expr - expr

Shift

Left-to-right

expr << expr

expr >> expr

Relational

Left-to-right

expr <

exprexpr > expr

expr <= expr

expr >= expr

Equality

Left-to-right

expr == expr

expr != expr

expr not_eq expr

Bitwise And

Left-to-right

expr & expr

expr bitand expr

Bitwise Exclusive Or

Left-to-right

expr ^ expr

expr xor expr

Bitwise Inclusive Or

Left-to-right

expr | expr

expr bitor expr

Logical And

Left-to-right

expr && expr

expr and expr

Logical Or

Left-to-right

expr || expr

expr or expr

Conditional

Right-to-left

expr ? expr : expr

Assignment

Right-to-left

lvalue = expr

lvalue op= expr

throw expr

throw

Comma (lowest precedence)

Left-to-right

expr , expr

3.5.1 Primary Expressions

A primary expression is the basic building block for more complex expressions. It is an expression in parentheses, a literal, or a name (possibly qualified). The various forms of primary expressions are:

literal

A constant value. String literals (being arrays of const char or const wchar_t) are lvalues. All other literals are rvalues. (See Chapter 1.)

this

Refers to the target object in a nonstatic member function. Its type is a pointer to the class type; its value is an rvalue.

( expression )

Has the type and value of expression.

unqualified-name

Names an entity according to the name lookup rules in Chapter 2. The expression result is the entity itself, and the expression type is the entity's type. The result is an lvalue if the entity is an object, data member, or function. The following are the various kinds of unqualified identifiers:

identifier

Names an object, function, member, type, or namespace. The name is looked up according to the rules in Chapter 2. The type is the declared type of the entity. If the entity is an object, data member, or function, the value is an lvalue.

operator symbol

Names an operator. See Chapter 5 for more information.

template-name < optional-template-args >

Names a template instance. See Chapter 7 for more information.

operator type

Names a type conversion operator. The type is a type specifier, possibly with one or more pointer symbols in the declarator. (See Chapter 2 for details about type specifiers and declarators.)

~ class-name

Names the destructor for class-name.

qualified-name

Uses the scope operator to qualify an identifier, operator, or destructor. The qualified name can be in the global scope or in the scope of a class or namespace:

: : identifier

Names a global identifier. The type is the declared type of the entity. If the entity is an object, data member, or function, the value is an lvalue; otherwise, it is an rvalue.

:: operator symbol

Names a global operator. Note that type conversion operators must be member functions, so there cannot be a global type conversion operator. See Chapter 5 for more information.

nested-name : : unqualified-name
nested-name :: template unqualified-name

Names an entity in a class or namespace scope. The nested-name can be a class or namespace name, or it can have the form class-or-namespace-name :: nested-name or class-name :: template nested-name. Use the template keyword when instantiating a template. See Chapter 7 for information about template members.

: : nested-name : : unqualified-name
: : nested-name :: template unqualified-name

Names an entity in a class or namespace scope. The first (left-most) class or namespace name is looked up in the global scope.

In the rest of this chapter, the syntax element name-expr refers to a qualified or unqualified name, as described in this section. In particular, a name-expr can be used to the right of the . or -> in a postfix expression.

Example 3-3 shows some primary expressions.

Example 3-3. Primary expressions
namespace ns {

  int x;

  class cls {

  public:

    cls(int);

    ~cls(  );

  };

}

int x;



3.14159         // Literal

(2 + 3 * 4)     // Parenthesized expression

x               // Unqualified identifier

ns::x           // Qualified identifier

ns::cls::cls    // Qualified constructor

operator*       // Unqualified operator name

3.5.2 Postfix Expressions

A postfix expression is an expression that uses postfix syntax (operator follows the operand) with some exceptions that just happen to have the same precedence. The postfix expressions are:

pointer [ expr ]

Returns an element of an array. The subscript operator requires a pointer as the left operand. An array is implicitly converted to a pointer. The right operand is converted to an integer, and the expression is evaluated as *((pointer) + (expr)). If the array index is out of bounds, the behavior is undefined. The result is an lvalue whose type is the base type of pointer.

expr ( optional-expr-list )

Calls a function. The function call operator requires one of the following as a left operand: a function name, an expression that returns a function pointer, or an expression that returns an object that has a function call operator. (An operator name is the same as a function name in this case.) The optional-expr-list is a comma-separated list of zero or more assignment expressions. (See Section 3.5.17 later in this chapter.) All the expressions in the expression list are evaluated, and then the function is called. The result type is the return type of the function. If the return type is a reference type, the result is an lvalue; otherwise, the result is an rvalue. If the return type is void, no value is returned. See Chapter 5 for more information about functions.

simple-type-specifier ( optional-expr-list )

Performs type conversion or construction. A simple-type-specifier is a single name: a fundamental type or a qualified name of a class, an enumeration, or a typedef. The result is an instance of the specified type, initialized as follows:

  • If the expression list is empty, the result is an rvalue that is initialized with a default constructor, or that is initialized to 0. (See Chapter 2 for details about initialization.)

  • If the expression list contains one expression, that value is cast to the desired type in the same manner as a cast expressionthat is, (type) expr. If the type is a reference, the result is an lvalue; otherwise, it is an rvalue.

  • If the expression list contains more than one expression, the type must be a class, and the expression list is passed as arguments to a suitable constructor to create an instance of the class, which is returned as an rvalue.

object . name-expr

Returns a member of an object. The name can be qualified to refer to a name in a base class (see Chapter 2). The return type is the type of name-expr. The return value depends on whether name-expr is a data member, member function, or enumerator:

  • If name-expr names a static data member, the member is returned as an lvalue.

  • If name-expr names a nonstatic data member, the result is an lvalue only if object is an lvalue. If name-expr is declared mutable, the result is not const even if object is const; otherwise, the result is const if either object or name-expr is const. Similarly, the result is volatile if either object or name-expr is volatile.

  • If name-expr names a member function, the usual rules for overload resolution apply (see Chapter 5). If the function is a static member function, the result is an lvalue. You can take the function's address (with &) or call the function.

  • If the function is a nonstatic member function, it must be used in a function callfor example, obj.memfun(arg).

  • If name-expr is an enumerator, the result is an rvalue.

pointer -> name-expr

Returns (*(pointer)).name-expr.

lvalue ++

Increments lvalue and returns its value prior to incrementing (as an rvalue). The type of lvalue must be arithmetic or a pointer. The new value is lvalue + 1.

If lvalue has type bool, the new value is always true. This bool-specific behavior is deprecated.

lvalue --

Decrements lvalue and returns its value prior to decrementing (as an rvalue). The type must be arithmetic or a pointer and cannot be bool. The new value is lvalue - 1.

const_cast< type >( expr )

Casts expr to type. If type is a reference, the result is an lvalue; otherwise, it is an rvalue. The new type must match the type of expr, but the const and volatile qualifiers can be changed.

A const_cast that removes a const qualifier is generally a bad idea. Nonetheless, it is sometimes necessary to cast away const-ness, especially when passing pointers to legacy libraries.

See Chapter 6 for a discussion of the mutable modifier, which lets you modify a data member of a const object.

dynamic_cast< type >( expr )

Casts a base class pointer or reference expr to a derived class type. A runtime check is performed to make sure the true class of expr is type or a class derived from type. The class must be polymorphic, that is, have at least one virtual function. The base class can be virtual. A dynamic_cast<> cannot cast away cv-qualifiers. The cast works as follows:

  • If type is void*, the return value is a pointer to the most-derived object that expr points to. The type does not have to be polymorphic in this case.

  • If expr is a pointer, type must be a pointer type. If the type of expr does not match type (is not the same as type or derived from type), a null pointer value is returned. Otherwise, the value of expr cast to type is returned. If expr is a null pointer, a null pointer is returned.

  • If expr is an object and type is a reference, expr is cast to type. If the type of expr does not match, a bad_cast exception is thrown.

  • You can also cast from a derived class to a base class, which is the same as an ordinary implicit conversion. The type does not have to be polymorphic in this case.

Example 3-4 shows some uses of dynamic_cast<>.

Example 3-4. Using dynamic_cast<>
#include <iostream>

#include <ostream>



class base {

public:

  virtual ~base(  ) {}

};



class derived : public base {};

class most_derived : public derived {};

class other : public base {};



int main(  )

{

  base* b = new derived;

  dynamic_cast<most_derived*>(b); // Null pointer

  dynamic_cast<derived&>(*b);     // OK

  dynamic_cast<other*>(b);        // Null pointer



  derived* d = new most_derived;

  b = d;

  b = dynamic_cast<base*>(d);    // OK, but dynamic_cast<>

                                 // is unnecessary.

}
figs/acorn.gifreinterpret_cast< type >( expr )

Casts expr to type. When using reinterpret_cast<>, no conversion functions or constructors are called. Casting to a reference yields an lvalue; otherwise, it yields an rvalue. Only the following conversions are allowed:

  • A pointer can be converted to an integer. The integer must be large enough to hold the pointer's value. Which integer type you should use is implementation-defined, as is the mapping from pointer to integer.

  • An integer or enumerated value can be converted to a pointer. The mapping is implementation-defined. Casting from a pointer to an integer back to the original pointer type yields the original pointer value, provided the integer type is large enough.

  • Casting an integer constant of value 0 to a pointer always produces a null pointer value. Casting any other integer expression of value 0 to a pointer produces an implementation-defined pointer, which may or may not be a null pointer.

  • A function pointer can be converted to a function pointer of a different type. Calling such a function results in undefined behavior. Converting back to the original pointer type produces the original pointer value.

  • An object pointer can be converted to an object pointer of a different type. Using such an object results in undefined behavior. Converting back to the original pointer type produces the original pointer value.

  • A pointer to a member can be converted to a pointer to a different member. Using the pointer to a member has undefined behavior, except that casting a pointer to a data member to a different pointer to a data member and back to the original type produces the original value, and casting a pointer to a member function to a different member function and back produces the original value.

  • A null pointer constant or value can be converted to a null pointer of the target type.

  • A reference can be cast in the same manner as a pointer (except that the pointer is dereferenced). That is, casting reinterpret_cast<T&>(x) is just like casting *reinterpret_cast<T*>(&x).

A reinterpret_cast has the following restrictions:

  • A function pointer cannot be converted to an object pointer, or an object pointer to a function pointer.

  • A member function pointer cannot be converted to a data member pointer, or a data member pointer to a member function pointer.

  • A member pointer cannot be converted to a nonmember pointer, or a nonmember pointer to a member pointer.

  • The target type must not cast away cv-qualifiers.

The need for reinterpret_cast<> is rare in an ordinary application.

Example 3-5 shows some uses of reinterpret_cast<>. The first use reinterprets the representation of a float as an int to show the underlying representation of a float. This use is implementation-dependent and requires sizeof(int) to be greater than or equal to sizeof(float). The second use is a simple conversion from a function pointer to an integer to print the address in a specific format. It requires that an int be large enough to hold a function pointer.

Example 3-5. Using reinterpret_cast<>
#include <cassert>

#include <iomanip>

#include <iostream>

#include <ostream>



int foo(  )

{

  return 0;

}



int main(  )

{

  using namespace std;

  float pi = 3.1415926535897;

  int   ipi;



  // Print numbers in pretty hexadecimal.

  cout << setfill('0') << showbase << hex << internal;



  // Show the representation of a floating-point number.

  assert(sizeof(int) == sizeof(float));

  ipi = reinterpret_cast<int&>(pi);

  cout << "pi bits=" << setw(10) << ipi << '\n';



  // Show the address of foo(  ).

  cout << "&foo=" << setw(10) <<

    reinterpret_cast<int>(&foo) << '\n';

}
static_cast< type >( expr )

Casts expr to type using a standard or user-defined conversion, as though you declared a temporary type tmp(expr) and used the value of tmp in place of the cast expression. The result is an lvalue if type is a reference; otherwise the result is an rvalue. A static_cast<> cannot cast away cv-qualifiers. The rules of permitted conversions are:

  • The type can be void, in which case the result of expr is discarded.

  • A base-class lvalue can be cast to a reference to a derived class, provided a standard conversion exists from a derived-class pointer to the base class. The base class must not be virtual. If expr is not actually a subobject of type, the behavior is undefined. (See dynamic_cast<> to learn how to make error-checking safe.)

  • A base-class pointer can be converted to a derived-class pointer in the manner described for class references.

  • A pointer to a member of a derived class can be converted to a pointer to a member of a base class if there is a standard conversion in the other direction. The target class (or an ancestor class) must contain the original member.

  • Standard arithmetic conversions can work in reversefor example, long can be cast to short. Integers can be cast to enumerations.

  • Enumerations can be cast to other enumerations.

  • A void pointer can be converted to any object pointer. Converting a pointer to void* (with the same cv-qualifiers as the original pointer) and back produces the original pointer value.

Example 3-6 shows some uses of static_cast<>.

Example 3-6. Using static_cast<>
#include <iostream>

#include <ostream>



class base {};

class derived : public base {};

class other : public base {};



enum color   { red, black };

enum logical { no, yes, maybe };



int main(  )

{

  base* b = new derived;

  static_cast<derived&>(*b); // OK

  static_cast<other*>(b);    // Undefined behavior



  derived* d = new derived;

  b = d;

  b = static_cast<base*>(d); // OK, but unnecessary



  color c = static_cast<color>(yes);



  int i = 65;

  std::cout << static_cast<char>(i);

}
typeid( expr )

Returns type information for the type of expr without evaluating expr. The type information is an lvalue of type const std::type_info (or an implementation-defined type that derives from type_info). See <typeinfo> in Chapter 13 for information about this class.

If expr is an lvalue of a polymorphic type (a class with at least one virtual function), the type information is for the most-derived class of expr. If expr is a dereference of a null pointer, bad_typeid is thrown.

If expr is not an lvalue, or the type is not polymorphic, the type information is for the static type of expr.

typeid( type )

Returns the type information for type as described for typeid( expr ). Example 3-7 shows some uses of typeid.

Example 3-7. Using typeid
#include <iostream>

#include <ostream>

#include <typeinfo>



class base {

public:

  virtual ~base(  ) {}

};



class derived : public base {};

enum color   { red, black };



// The actual output is implementation-defined, but should reflect the types

// shown in the comments.

int main(  )

{

  base* b = new derived;

  std::cout << typeid(*b).name(  ) << '\n';      // Derived

  std::cout << typeid(base).name(  ) << '\n';    // Base

  derived* d = new derived;

  std::cout << typeid(*d).name(  ) << '\n';      // Derived

  std::cout << typeid(derived).name(  ) << '\n'; // Derived

  std::cout << typeid(red).name(  ) << '\n';     // Color

  std::cout << typeid(color).name(  ) << '\n';   // Color

}

3.5.3 Unary Expressions

A unary expression uses a unary prefix operator:

++ lvalue

Increments lvalue, which must be of arithmetic or pointer type, and returns the new value as an lvalue. The expression ++x is equivalent to x += 1, unless x is of type bool, in which case the expression ++x is equivalent to x=true. The special-case behavior for bool is deprecated.

-- lvalue

Decrements lvalue, which must be of arithmetic or pointer type (not bool), and returns the new value as an lvalue. The expression --x is equivalent to x -= 1.

* pointer

Dereferences pointer and returns an lvalue for the object that pointer points to. If pointer has type T*, the expression has type T (preserving any cv-qualifiers).

& lvalue
& qualified-name

Returns the address of lvalue or qualified-name. If lvalue has type T or if qualified-name is a static member of type T, the result is the object's address, which is an rvalue of type pointer to T. If qualified-name is a nonstatic member of class C, the result type is a pointer to a member of class C.

Note that a pointer to a member is formed only by applying the & operand to a qualified name. Even in the scope of a class, &unqualified-name is not a pointer to a member, but an ordinary pointer to an object.

You cannot take the address of a bit-field. To take the address of an overloaded function, the context must make it clear which function you mean. Example 3-8 shows uses of the & operator.

Example 3-8. Using the & operator
class demo

{

public:

  int x;

  static int y;

  int get_x(  ) { return x; }

};



int demo::y = 10;



int add(int a, int b) { return a + b; }

double add(double a, double b) { return a + b; }



int main(  )

{

  demo d;

  int demo::*p;

  int (demo::*func)(  );

  int *i;

  int local = 42;

  int *ptr = &local;



  p = &demo::x;

  i = &demo::y;

  func = &demo::get_x;

  d.*p = *ptr;

  *i = (d.*func)(  );



  int (*adder)(int, int);

  adder = &add;

  d.*p = adder(42, *i);



  return d.y;

}
+ expr

Returns expr, which must have arithmetic, enumeration, or pointer type. The usual type promotions take place, and the result type is the promoted type. The result is an rvalue.

- expr

Negates expr, which must have arithmetic or enumerated type. The usual type promotions take place, and the result type is the promoted type. The result is an rvalue. If the type is unsigned, the result is 2n - expr, in which n is the number of bits in the result type.

~ expr
compl expr

Returns the bitwise complement of expr, which must have integral or enumeration type. The type is promoted according to the usual rules, and the result type is the promoted type. The result is an rvalue. Every zero bit in expr is converted to a one bit, and every one bit is converted to a zero bit.

In the ambiguous case of ~C( ), in which C is a class name, ~C( ) is interpreted as the complement operator, not the destructor of C. If C does not have an overloaded operator~ or an implicit conversion to an integral or enumerated type, the expression ~C( ) results in an error. To force a reference to C's destructor, use a member reference (e.g., this->~C( )) or a qualified name (e.g., C::~C( )).

! expr
not expr

Returns the logical negation of expr after converting it to bool. The result is an rvalue of type bool. If expr is true, the result is false; if expr is false, the result is true.

sizeof expr
sizeof ( type )

Returns the size in bytes of type or the type of expr (without evaluating expr). By definition, sizeof(char) == 1. You cannot take the size of a bit-field, a function, or an incomplete type. The size of a reference is the size of the referenced type.

The sizeof operator always returns a value greater than zero for a class or object of class type. The size of a base-class subobject within a derived-class object can be zero, so the compiler is not necessarily wasting space. You can see this in Example 3-9, which shows that the size of the derived class is the same as the size of the base class. The expression result is an rvalue of type size_t. (See <cstdlib> in Chapter 13.)

Example 3-9. Using the sizeof operator
#include <iostream>

#include <ostream>



class base {};

class derived : public base {};



int main(  )

{

  // The values actually printed depend on the implementation, but many

  // common implementations print the values shown.

  using namespace std;

  cout << sizeof(base)  << '\n';      // Prints 1

  cout << sizeof(derived)  << '\n';   // Prints 1

  base b[3];

  cout << sizeof(b) << '\n';          // Prints 3

  derived d[5];

  cout << sizeof(d) << '\n';          // Prints 5

}
new type
new type ( optional-expr-list )
new ( expr-list) type
new ( expr-list) type ( optional-expr-list )

Allocates and initializes a dynamic object or array of objects. The new expression first calls an allocation function (operator new) to allocate memory. It then constructs the object in the allocated memory. A class can provide its own allocation function by overriding operator new as a member function. Otherwise, a global operator new is called. (See <new> in Chapter 13 for the standard allocation functions.)

The parts of a new expression are:

new
::new

The new keyword can be prefixed with the global scope operator to call the global operator new as the allocation function.

( expr-list )

An expression list in parentheses is called the placement. The placement is optional, but if it is used, it must contain at least one expression. If present, the expressions are passed directly to the allocation function without further interpretation.

The standard library defines two placement new functions, which are discussed in Chapter 13. You can also write your own overloaded operator new functions for other forms of placement new. The first operand to operator new is always the amount of memory to allocate followed by the placement parameters.

type

The type to allocate. It has the following form (optionally in parentheses):

type-specifiers ptr-operators dimensions

(See Chapter 2 for information about type specifiers.) The ptr-operators are optional and can be * or & for pointers or references. The array dimensions are optional. All dimensions of an array except the first must be constant integral expressions (enclosed in square brackets). The first dimension can be any integral expression.

The compiler reads the longest sequence of declarators as the type, even if it results in a syntax error. If type contains parentheses (e.g., a function pointer) or if you want to force a particular type declaration, surround type with parentheses. For example, (new int[n])[2] allocates an array of n integers and extracts the element at index 2. Without the parentheses, new int[n][2] allocates a two-dimensional array of int.

( optional-expr-list )

An optional initializer that follows the usual rules for initializers. (See Chapter 2 for more information about initializers.)

If the expression list has a single expression, and a single object is allocated, the expression is the object's initial value.

If multiple expressions are in the initializer, the type must be a class type, and the expression list is passed to a suitable constructor, which is found by the usual rules for resolving overloaded functions (Chapter 5).

An array cannot have an initializer. If the base type is a POD type, the array elements are uninitialized; otherwise, they are initialized by calling the default constructor for each array element.

See Chapter 6 for a comparison of POD and non-POD types.

The allocation function (operator new) is called with at least one argument: the number of bytes to allocate (of type size_t). If a placement is used, the placement arguments are passed as additional arguments to the allocation function to the right of the size. If the allocation function cannot fulfill the request, it typically throws std::bad_alloc. However, if you pass std::nothrow as the placement argument, the standard allocation function returns a null pointer as an error indicator instead of throwing bad_alloc.

figs/acorn.gif

Allocating an array is different from allocating a scalar. The allocation function is operator new[]. The requested size is the number of elements in the array times the size of each element. An implementation is free to request additional memory for its own use, perhaps to store the number of elements in the array. The amount of additional memory is implementation-defined. Even if the array size is zero, the returned pointer is not null. The allocated memory is aligned to the most restrictive boundary for any type. (More precisely, the allocation function must return a pointer that is aligned for any type, and the new expression simply uses that pointer.) Thus, you can, for example, allocate an array of char and use the memory to store any object. The standard containers often do this. See <memory> in Chapter 13 for algorithms that work with uninitialized memory.

If an exception is thrown during initialization, the memory is freed by calling a corresponding deallocation function (corresponding to the equivalent delete expression). If placement new is used, a placement delete operator with the same additional parameters is the deallocation function, if such a function exists; otherwise, no deallocation function is called.

The following are some examples of new expressions:

int n = 10;                 // Note that n is not const

new int                     // Pointer to uninitialized int

new int(  )                      // Pointer to int, initialized to 0

new int[n]                  // n uninitialized ints

new (int*)                  // Pointer to uninitialized pointer to int

new (int (*[n])(  ))                          // n function pointers

  typedef int (*int_func)(  );

new int_func[n];            // n function pointers

new (int*[n][4])            // n4 array of pointers to int

new complex<int>(42)        // Pointer to a complex object

new complex<int>[5]         // Five default-initialized complex objects
delete pointer
delete[] pointer

Destroys and frees a dynamic object or array of objects and returns void. The actual memory deallocation is performed by a deallocation function. A class can provide its own deallocation function by overriding operator delete. A plain delete expression looks up the deallocation function first in the class (if the pointer type is a pointer to a class type) and, if it is not found, in the global scope. Use the global scope operator to look only in the global scope. (See <new> in Chapter 13 for the default deallocation functions.)

To free an array, you must use delete[]. To free a scalar, you must use delete. If you make a mistake, the results are undefined. Note that the compiler cannot generally help you avoid mistakes because a pointer to a scalar cannot be distinguished from a pointer to an array. (Some libraries are more forgiving of this error than others.)

The pointer expression is evaluated once. If pointer is a pointer to a class type, the scalar form calls the object's destructor first, and the array form calls the destructor for each element of the array. The value of pointer is then passed to the deallocation function as a void*. If the expression's static type does not match the object's dynamic type, the static class must have a virtual destructor, or else the behavior is undefined. See Chapter 6 for more information.

You can delete a pointer to a const object. It is also safe to delete a null pointer value, in which case the deallocation function does nothing.

3.5.4 Cast Expressions

A cast expression performs an explicit type conversion. The cast expression is a holdover from C. In C++, the preferred cast syntax uses one of the explicit cast operators (described in Section 3.5.2 earlier in this chapter). The C-style casts are still used for their brevity, however.

( type ) expr

The C-style cast converts expr to type using one or more template-like type conversions. If type is a reference, the result is an lvalue; otherwise the result is an rvalue. The following type conversions are tried in order. The first one that is syntactically allowed is the one used, even if the expression is not permitted semantically.

  1. const_cast< type >( expr )

  2. static_cast< type >( expr )

  3. const_cast< type >( static_cast< type1 >( expr ))

  4. reinterpret_cast< type >( expr )

  5. const_cast< type >( reinterpret_cast< type1 >( expr ))

The type type1 is the same as type, but its cv-qualifiers are changed to match the type of expr. Thus, the C-style type cast can mix a const cast with a static or reinterpret cast. A C-style cast can also cast to an otherwise inaccessible base class (see Chapter 6 for information about accessibility). That is, you can cast a derived class to an inaccessible base class, cast a pointer to a member of a derived class to a pointer to a member of an inaccessible base class, or cast from an inaccessible base class to an accessible derived class.

3.5.5 Pointer-to-Member Expressions

A pointer-to-member expression takes an object or a pointer to an object as the lefthand operand and a pointer-to-member as the righthand operand, and it binds the pointer-to-member to the object. The result can be a data member or a member function. A member function can be used only to call the function. Example 3-8 shows some uses of pointers to members. The pointer-to-member operator has the following syntax:

object .* expr

Binds expr to object, in which expr is a pointer-to-member of class C, and the type of object is C or a class derived from C. The result is an lvalue if object is an lvalue and expr points to a data member; otherwise, the result is an rvalue. The type of the result is determined by the type of expr. The behavior is undefined if expr is a null pointer-to-member.

pointer ->* expr

Binds expr to the object that pointer points to, in which expr is a pointer-to-member of class C, and the type of object is C or a class derived from C. The result is an lvalue if expr points to a data member. The type of the result is determined by the type of expr. The behavior is undefined if pointer is null or if expr is a null pointer-to-member.

If expr points to a virtual function, the usual rules apply. That is, the actual function called is that of the most-derived type of *pointer or object. See Chapter 6 for more information about virtual functions.

3.5.6 Multiplicative Expressions

figs/acorn.gif

A multiplicative expression is used for multiplication, division, and remainders. The multiplicative operators require arithmetic or enumeration types; the usual conversions are performed, and an rvalue is returned. If the result is too large, the behavior is undefined (except for unsigned types, for which arithmetic is performed modulo the integer size; see Chapter 1 for details). Many C++ implementations ignore integer overflow. The multiplicative operators have the following syntax:

expr1 * expr2

Performs multiplication.

expr1 / expr2

Performs division. If the divisor is zero, the behavior is undefined.

figs/acorn.gifexpr1 % expr2

Returns the remainder of dividing expr1 by expr2. The operands must have integral or enumerated types. If expr2 is 0, the behavior is undefined; otherwise, the value is such that (a/b)*b + a%b == a. If both operands are nonnegative, the result is nonnegative; otherwise, the sign of the result is implementation-defined.

3.5.7 Additive Expressions

figs/acorn.gif

An additive expression is used for addition and subtraction. The additive operators require arithmetic, enumerated, or pointer types. The usual conversions are performed, and an rvalue is returned. If the result of an additive expression is too large, the behavior is undefined (except for unsigned types, for which arithmetic is performed modulo the integer size; see Chapter 1 for details). Many C++ implementations ignore integer overflow. The additive operators have the following syntax:

expr + expr

Performs addition. If one operand has a pointer type, the other must have an integral or enumeration type. The result is a pointer to the same array, but with the index offset by N positions (N can be positive, negative, or 0), in which N is the integral operand. The resulting pointer must be within the bounds of the array or point to one past the end of the array; otherwise, the behavior is undefined. Note that a pointer to any object can be treated as a one-element array.

expr - expr

Performs subtraction. If both operands have arithmetic or enumeration types, the usual promotions apply and the result is the difference of the operands.

If both operands are pointers, they must point to elements of the same array, or to one element past the end of the array. The result has type ptrdiff_t (declared in <cstdlib>) and is equal to the difference of the indices of the two objects.

If the left operand is a pointer and the right operand has integral or enume