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 |
|
Postfix | Left-to-right |
|
Unary | Right-to-left |
|
Cast | Right-to-left |
|
Pointer to Member | Left-to-right |
|
Multiplicative | Left-to-right |
|
Additive | Left-to-right |
|
Shift | Left-to-right |
|
Relational | Left-to-right |
|
Equality | Left-to-right |
|
Bitwise And | Left-to-right |
|
Bitwise Exclusive Or | Left-to-right |
|
Bitwise Inclusive Or | Left-to-right |
|
Logical And | Left-to-right |
|
Logical Or | Left-to-right |
|
Conditional | Right-to-left |
|
Assignment | Right-to-left |
|
Comma (lowest precedence) | Left-to-right |
|
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:
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 }
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 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.)
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]) // 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.
(
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.
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.
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.
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.
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.
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:
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:
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:
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.
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.
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.