DekGenius.com
Team LiB   Previous Section   Next Section

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 expression—that 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 call—for 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 reverse—for 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 enumeration type, the result is the same as expr1 - expr2.

3.5.8 Shift Expressions

A shift expression shifts the bits of the left operand by an amount specified by the right operand. The operands must be have integral or enumerated types; both types are promoted to integral types. The result type is the promoted type of the left operand.

figs/acorn.gif

The result of a shift operation is undefined if the right operand is negative or is larger than the number of bits in the left operand.

The shift operators have the following syntax:

expr1 << expr2

Performs a left shift of expr1 by expr2 bits. Vacated bits are filled with zeros. If expr1 is unsigned, the result is equal to multiplying expr1 by 2 raised to expr2 (modulo the integer size; see Chapter 1 for more information about unsigned integer arithmetic).

figs/acorn.gifexpr1 >> expr2

Performs a right shift of expr1 by expr2 bits. If the expr1 is unsigned, or if it is signed and has a positive value, vacated bits are filled with zeros. The result is equal to dividing expr1 by 2 raised to expr2 (truncated to an integer). If expr1 has a signed type and negative value, the result is implementation-defined.

The standard library overloads the shift operators for the I/O stream class templates. As with any overloaded function, the syntax (precedence and associativity) remains the same. Only the runtime behavior is different. See Chapter 9 for more information.

3.5.9 Relational Expressions

A relational expression compares two values for relative order. Relational operators have higher precedence than equality operators, so the following two expressions are equivalent:

a < b == c > d
(a < b) == (c > d)

The result of a relational expression is an rvalue of type bool. The operands must have arithmetic, enumeration, or pointer types. For arithmetic and enumeration types, the usual conversions are performed, and the resulting values are compared.

When comparing pointers, the pointers must have the same types (after the usual conversions and ignoring cv-qualification), one must be a pointer to void, or one operand must be a null pointer constant. If the pointer types are compatible, they are compared as follows (L is the left operand and R is the right operand):

  • If L and R point to the same function, the same object, or one element past the end of the same array, or if both are null pointers, they are considered to be equal to each other. That is, L <= R and L >= R are true, and L < R and L > R are false.

  • figs/acorn.gif

    If L and R point to different objects (that are not members of a common object and not elements of the same array), different functions, or if only one is a null pointer, the result of the relational operators depends on the implementation.

  • If L and R point to data members (as ordinary pointers, not pointers-to-members) within the same object (members of the same object, elements of arrays that are data members, and so on, recursively applied), and if the members are not separated by an access specifier label, and if the object is not a union, then L > R if the member to which L points is declared later than the member to which R points. Similarly, L < R if the member to which L points is declared earlier than the member to which R points. If the members are separated by an access specifier label, the results are implementation-dependent.

  • If L and R point to data members (as ordinary pointers, not pointers-to-members) of the same union, they are considered to be equal to each other.

  • If L and R point to elements of the same array, including one element past the end of the same array, the pointer with the higher subscript is larger.

Example 3-10 shows some pointer comparisons.

Example 3-10. Comparing pointers
#include <iostream>
#include <ostream>

struct Demo {
  int x;
  int y;
};

union U {
  int a;
  double b;
  char c[5];
  Demo d;
};

int main(  )
{
  Demo demo[10];
  std::cout << std::boolalpha;
  // Everything prints "true".
  std::cout << (&demo[0]   < &demo[2])   << '\n';
  std::cout << (&demo[0]   == demo)      << '\n';
  std::cout << (&demo[10]  > &demo[9])   << '\n';
  std::cout << (&demo[0].x < &demo[0].y) << '\n';

  U u;
  std::cout << (&u.d == static_cast<void*>(u.c)) << '\n';
  std::cout << (&u.a == static_cast<void*>(&u.b)) << '\n';
}

The relational operators have the following syntax:

expr1 < expr2

Returns true if expr1 is less than expr2

expr1 > expr2

Returns true if expr1 is greater than expr2

expr1 <= expr2

Returns true if expr1 is less than or equal to expr2

expr1 >= expr2

Returns true if expr1 is greater than or equal to expr2

3.5.10 Equality Expressions

An equality expression compares two values to see if they are equal or different. Equality operators have lower precedence than relational operators, so the following two expressions are equivalent:

a < b == c > d
(a < b) == (c > d)

The result of an equality expression is an rvalue of type bool. The operands must have arithmetic, enumeration, or pointer types. For arithmetic and enumeration types, the usual conversions are performed, and the resulting values are compared.

Note that comparing the results of floating-point values for equality rarely gives the result you want. Instead, you probably want a fuzzy comparison that allows for floating-point imprecision.

When comparing pointers, the pointers must have the same type (after the usual conversions). The pointers are equal if any of the following conditions hold, and are not equal if none of the conditions hold:

  • Both pointers are null pointers.

  • Both object pointers point to the same object.

  • Both object pointers point to one element past the end of the same array.

  • Both function pointers point to the same function.

  • Both member pointers point to the same member of the same most-derived object.

  • Both member pointers point to any data members of the same union.

The equality operators have the following syntax:

expr == expr

Returns true if the operands are equal.

expr != expr
expr not_eq expr

Returns false if the operands are equal.

3.5.11 Bitwise And Expressions

A bitwise and expression performs and on its operands' bits. The bitwise and expression is permitted only on integral types after the usual arithmetic conversions. The bitwise and operator has the following syntax:

expr & expr
expr bitand expr

Performs bitwise and of the operands. Each bit of the result is 1 if the corresponding bits of the operands are both 1; otherwise, the result bit is 0.

3.5.12 Bitwise Exclusive Or Expressions

A bitwise exclusive or expression performs exclusive or on its operands' bits. The bitwise exclusive or expression is permitted only on integral types after the usual arithmetic conversions. The exclusive or operator has the following syntax:

expr ^ expr
expr xor expr

Performs bitwise exclusive or of the operands. Each bit of the result is 1 if the corresponding bits of the operands are not equal; otherwise, the result bit is 0.

3.5.13 Bitwise Inclusive Or Expressions

A bitwise inclusive or expression performs inclusive or on its operands' bits. The bitwise inclusive or expression is permitted only on integral types after the usual arithmetic conversions. The bitwise inclusive or operator has the following syntax:

expr | expr
expr bitor expr

Performs bitwise inclusive or of the operands. Each bit of the result is 0 if the corresponding bits of the operands are both 0; otherwise, the result bit is 1.

3.5.14 Logical And Expressions

A logical and expression implicitly converts its operands to type bool. The result has type bool: true if both operands are true; otherwise, it is false. The logical and operator is a short-circuit operator, so the second operand is evaluated only if the first evaluates to true. The logical and operator has the following syntax:

expr && expr
expr and expr

Performs the logical and of the operands.

3.5.15 Logical Or Expressions

A logical or expression implicitly converts its operands to type bool. The result has type bool: false if both operands are false; otherwise, it is true. The logical or operator is a short-circuit operator, so the second operand is evaluated only if the first evaluates to false. The logical or operator has the following syntax:

expr || expr
expr or expr

Performs the logical or of the operands.

3.5.16 Conditional Expressions

A conditional expression is like an if statement in an expression:

condition ? true-expr : false-expr

The first operand is converted to bool. If the value is true, only the second operand is evaluated; if it is false, only the third operand is evaluated. The result of the conditional expression is the result of the second or third operand, whichever is evaluated.

The type of the expression depends on the types of the second and third operands: if both operands are lvalues of the same type, the result is an lvalue of that type. If one operand is an lvalue of type T, and the other operand is an lvalue that can be implicitly converted to a reference to T, the result is an lvalue of type T. An error results if the conversion is ambiguous—that is, the second operand can be converted to the type of the third just as readily as the third can be converted to the type of the second. Otherwise, the result is an rvalue, and the type is determined as follows:

  • If both operands have the same type, that is the type of the result.

  • If one operand is a throw expression, the result type is that of the other operand.

  • If one type S can be implicitly converted to the other type T, T is the result type. An error results if each operand can be implicitly converted to the other type.

3.5.17 Assignment Expressions

An assignment expression assigns its right operand to its left operand. In addition to plain assignment (x = y), there are several other assignment operators, each of which is a shorthand for an arithmetic operation and an assignment. The left operand of an assignment expression must be a modifiable lvalue. The result is an lvalue: the left operand after the assignment is complete.

Assignment operators have the following syntax:

lvalue = expr

Assigns expr to lvalue. If the left operand has class type, a suitable assignment operator is called. (See Chapter 6 for more information.) If the left operand is not of class type, and the operands have different types, the right operand is converted to the type of the left operand.

lvalue += expr
lvalue -= expr

The assignment operator x op= y is shorthand for x = x op y, except that x is evaluated only once. The type of x must be an arithmetic type or a pointer type. The usual conversions are performed for op, and the result is converted to the type of x.

lvalue *= expr
lvalue /= expr
lvalue %= expr
lvalue <<= expr
lvalue >>= expr
lvalue &= expr
lvalue and_eq expr
lvalue ^= expr
lvalue xor_eq expr
lvalue |= expr
lvalue or_eq expr

The assignment operator x op= y is shorthand for x = x op y, except that x is evaluated only once. The type of x must be arithmetic (unlike the additive assignment operators, which allow pointers because the additive operators allow pointer operands). The usual conversions are performed for op, and the result is converted to the type of x.

throw expr
throw

Throws an exception. The first form throws expr as the exception object. The second form rethrows the current exception object. If a program uses the second form, and no exception is being handled, throw calls terminate( ). (See <exception> in Chapter 13 for more information about terminate.)

You can throw any expression, but the convention is to throw an object of type exception or of a derived class, especially a class derived from one of the standard exception classes (see <stdexcept> in Chapter 13).

figs/acorn.gif

The throw expression initializes a temporary exception object, and that object is thrown to the exception handler, which can copy the exception object in its catch clause. When the handler finishes, the object is destroyed. An implementation can optimize away the extra copy and initialize the catch object directly with the exception expr. Even if the object is never copied, the class must have a copy constructor.

See the try statement in Chapter 4 for a description of how exceptions are handled and for examples of throw expressions. See also Chapter 5 for information about throw specifications in function declarations.

3.5.18 Comma Expressions

A comma expression serializes expression evaluation. The comma operator evaluates its left operand, discards the result, and then evaluates the right operand. The result is the value of the right operand, and the type is the type of the right operand. If the right operand is an lvalue, the result is an lvalue; otherwise, the result is an rvalue. Typically, the left operand is evaluated for a side effect, such as an assignment or a function call.

The comma operator can be overloaded, in which case the serial nature is lost. Both operands are evaluated, and the operator function is called. Overloading the comma operator is seldom done.

The syntax of the comma operator is:

expr1 , expr2

Evaluates expr1, then expr2, and returns expr2. To use a comma expression as a function argument or in other contexts in which a comma is significant, enclose the comma expression in parentheses:

sin((angle = 0.0, angle + 1.0));
    Team LiB   Previous Section   Next Section