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
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.
}
- 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 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.
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.
const_cast< type
>( expr
)
static_cast< type
>( expr
)
const_cast< type
>( static_cast<
type1 >(
expr ))
reinterpret_cast<
type >(
expr )
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
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.
- expr1 % 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
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.
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).
- expr1 >> 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.
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).
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));
|