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
                        

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.

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:

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

Using dynamic_cast<> reinterpret_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 reinterpret_cast has the following restrictions:

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.

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:

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

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.

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.

+ 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 2 n - 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.)

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.

Using the sizeof operator

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])            // n∞4 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.

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.

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.

Multiplicative Expressions

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:

Additive Expressions

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.

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.

Shift Expressions

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:

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.

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):

Example 3-10 shows some pointer comparisons.

The relational operators have the following syntax:

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.

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:

The equality operators have the following syntax:

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:

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:

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:

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:

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:

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

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).

Assignment Expressions

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.

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: