DekGenius.com
Team LiB   Previous Section   Next Section

3.2 Type Conversions

In an arithmetic expression, binary operators require operands of the same type. If this is not the case, the type of one operand must be converted to match that of the other operand. When calling a function, the argument type must match the parameter type; if it doesn't, the argument is converted so its type matches. C++ has cast operators, which let you define a type conversion explicitly, or you can let the compiler automatically convert the type for you. This section presents the rules for automatic type conversion.

3.2.1 Arithmetic Types

An arithmetic type is a fundamental integral or floating-point type: bool, char, signed char, unsigned char, int, short, long, unsigned int, unsigned short, unsigned long, float, double, or long double. Some operations are permitted only on arithmetic types, pointer types, enumerations, class types, or some combination of types. The description of each operator tells you which types the operator supports.

3.2.2 Type Promotion

Type promotion is an automatic type conversion that applies only to arithmetic types, converting a "smaller" type to a "larger" type while preserving the original value. Contrast promotions with other automatic type conversions (described in later subsections), which can lose information. Promotions involve either integral or floating-point values. The rules for integral promotion are:

  • A "small" integral rvalue is converted to an int if the type int can represent all of the values of the source type; otherwise, it is converted to unsigned int. A "small" value is an integral bit-field (see Chapter 6) whose size is smaller than an int, or a value with one of the following types: char, signed char, unsigned char, short int, unsigned short int.

  • An rvalue whose type is wchar_t or an enumeration (including bit-fields with enumeration type) is converted to the first type that can hold all the values of the source type. The type is one of: int, unsigned int, long, or unsigned long.

  • An rvalue of type bool can be converted to an int; the value true becomes 1, and the value false becomes 0.

One floating-point promotion rule is defined:

  • An rvalue of type float can be converted to type double.

3.2.3 Arithmetic Type Conversions

An arithmetic type conversion is an automatic type conversion that the compiler applies to the operands of the built-in arithmetic and comparison operators. For the arithmetic operators, the result type is the same as the operand type.

figs/acorn.gif

The arithmetic type conversions try to preserve the original values as much as possible. However, they do not always succeed. For example, -1 / 1u does not produce -1 as a result because -1 has type int, which is converted to type unsigned int, yielding an implementation-defined arithmetic value. On a 32-bit, two's-complement system, the result is 4294967295u.

The rules for arithmetic type conversion are applied in the following order:

  1. If one operand has type long double, the other is converted to long double.

  2. Otherwise, if one operand is double, the other is converted to double.

  3. Otherwise, if one operand is float, the other is converted to float.

  4. Otherwise, integral promotions are performed (see the section Section 3.2.2).

  5. After integral promotion, if one operand is unsigned long, the other is converted to unsigned long.

  6. Otherwise, if one operand is long, and the other is unsigned int, type conversion occurs as follows:

    1. If all values of type unsigned int fit in a long int, the unsigned int is converted to long int.

    2. Otherwise, both operands are converted to unsigned long.

  7. Otherwise, if one operand is long, the other is converted to long.

  8. Otherwise, if one operand is unsigned, the other is converted to unsigned.

  9. Otherwise, both operands are int.

3.2.4 Implicit Numeric Conversions

An arithmetic value can be converted implicitly to a different arithmetic value, even if that conversion loses information. These implicit conversions can take place in assignments, initializers, arguments to function calls, returning values from a function, and template arguments. Do not confuse these conversions with the arithmetic type conversions described earlier, which can take place in any arithmetic operation.

The basic rule is that any arithmetic type can be converted to any other. If the destination type cannot hold the converted value, the behavior is undefined. When assigning a floating-point number to an integer, the floating-point value is truncated toward zero by discarding the fractional part. When converting an integer or enumerated value to a floating-point number, if the integer value falls between two floating-point values (that is, the integer is not representable in the floating-point format), the implementation chooses one of the two neighboring values.

A value can be converted to a class type if the class has a suitable constructor that does not use the explicit specifier. A value of class type can be converted to a non-class type if the source class has a type conversion operator of the target type. When converting a class type to another class type, the compiler considers the constructors for the target class and the type conversion operators for the source class.

3.2.5 Lvalue Conversions

Lvalue conversions automatically convert an lvalue to an rvalue for contexts in which an rvalue is required. The need to convert an lvalue to an rvalue is usually transparent, and these conversions are listed only for the sake of completeness:

  • An array lvalue can be converted to a pointer rvalue, pointing to the first element of the array.

  • A function lvalue can be converted to an rvalue pointer to the function.

  • Any other lvalue can be converted to an rvalue with the same value. If the type is not a class type, cv-qualifiers are removed from the type. Thus, a const int lvalue is converted to an int rvalue.

3.2.6 Conversion to bool

Arithmetic, enumeration, and pointer values can be converted to bool, in which null pointers and zero arithmetic values are false, and everything else is true. A common idiom is to test whether a pointer is a null pointer:

if (ptr)
  ... // Do something with *ptr.
else
  ... // Pointer is null

if (! ptr)
  // Another way to test if ptr is null

This idiom is so common that some classes have operators to convert an object to void*, so void* can be converted to bool. (Any pointer type would do, but void* is used because the pointer is not meant to be dereferenced, only converted to bool.) The void* operator is used instead of a direct conversion to bool to avoid automatic promotion to an integer. For example, basic_ios defines operator void*( ) to return a null pointer if the iostream's failbit is set. (See <ios> in Chapter 13 for details.)

3.2.7 Type Casts

A type cast is an explicit type conversion. C++ offers several different ways to cast an expression to a different type. The different ways expose the evolution of the language. The six forms of type cast expressions are:

  • (type) expr

  • type ( expr )

  • const_cast< type >( expr )

  • dynamic_cast< type >( expr )

  • reinterpret_cast< type >( expr )

  • static_cast< type >( expr )

The first form was inherited from C; the second form was added in the early days of C++ and has the same meaning as the first, but with a slightly different syntax. The final four forms supplant the two older forms and are preferred because they are more specific, thus reducing the risk of error. All type cast expressions are described in detail later in this chapter; the first form is covered in the section Section 3.5.4, and the other five forms are covered in Section 3.5.2. The remainder of this section briefly discusses when to use each form.

If you simply want to force an expression to be const, or to remove a const (e.g., prior to passing a pointer to a legacy library that is not written in C++ and does not know about const declarations), use const_cast<>. (You can also change the volatile qualifier, but that is less common.)

If you have a pointer or reference to an object whose declared type is a base class, but you need to obtain a derived class pointer or reference, you should use dynamic_cast<>. The dynamic cast performs a runtime check to make sure the cast is valid. A dynamic cast works only with polymorphic classes, which have at least one virtual function.

The common uses of static_cast<> are to force one enumeration to a different enumerated type, force an arithmetic type to a different type, or force a particular conversion from a class-type object that has multiple type conversion operators. The simpler type casts are sometimes used in these cases, for the sake of brevity. For example, sqrt(float(i)) can be easier to read than sqrt(static_cast<float>(i)). Use static_cast to reverse any implicit type conversion. For example, C++ automatically converts an enum to an integer; use static_cast if you want to convert an integer to an enum. You can use static_cast to cast a pointer to and from void*.

A reinterpret_cast<> is reserved for potentially unsafe type casts, such as converting one type of pointer to another. You can also convert a pointer to an integer or vice versa. For example, an internal debugging package might record debug log files. The logger might convert pointers to integers using reinterpret_cast<> and print the integers using a specific format.

If you try to perform the wrong kind of cast with one of the template-like cast operators, the compiler informs you of your mistake. For example, if you use static_cast<> and accidentally cast away const-ness, the compiler complains. Or if you use static_cast<> where you should have used reinterpret_cast<>, the compiler complains. The short forms of casting do not provide this extra level of error-checking because one form must suffice for the several different kinds of type casts.

When you see a type cast, you should read it as a warning that something unusual is happening. The longer forms of type casts provide additional clues about the programmer's intent, and help the compiler enforce that intent.

    Team LiB   Previous Section   Next Section