4.11.3. Explicit Conversions

Sometimes we want to explicitly force an object to be converted to a different type. For example, we might want to use floating-point division in the following code:

int i, j;
double slope = i/j;

To do so, we’d need a way to explicitly convert i and/or j to double. We use a cast to request an explicit conversion.


Image Warning

Although necessary at times, casts are inherently dangerous constructs.


Named Casts

A named cast has the following form:

cast-name<type>(expression);

where type is the target type of the conversion, and expression is the value to be cast. If type is a reference, then the result is an lvalue. The cast-name may be one of static_cast , dynamic_cast , const_cast , and reinterpret_cast . We’ll cover dynamic_cast, which supports the run-time type identification, in § 19.2 (p. 825). The cast-name determines what kind of conversion is performed.

static_cast

Any well-defined type conversion, other than those involving low-level const, can be requested using a static_cast. For example, we can force our expression to use floating-point division by casting one of the operands to double:

// cast used to force floating-point division
double slope = static_cast<double>(j) / i;

A static_cast is often useful when a larger arithmetic type is assigned to a smaller type. The cast informs both the reader of the program and the compiler that we are aware of and are not concerned about the potential loss of precision. Compilers often generate a warning for assignments of a larger arithmetic type to a smaller type. When we do an explicit cast, the warning message is turned off.

A static_cast is also useful to perform a conversion that the compiler will not generate automatically. For example, we can use a static_cast to retrieve a pointer value that was stored in a void* pointer (§ 2.3.2, p. 56):

void* p = &d;   // ok: address of any nonconst object can be stored in a void*
// ok: converts void* back to the original pointer type
double *dp = static_cast<double*>(p);

When we store a pointer in a void* and then use a static_cast to cast the pointer back to its original type, we are guaranteed that the pointer value is preserved. That is, the result of the cast will be equal to the original address value. However, we must be certain that the type to which we cast the pointer is the actual type of that pointer; if the types do not match, the result is undefined.

const_cast

A const_cast changes only a low-level (§ 2.4.3, p. 63) const in its operand:

const char *pc;
char *p = const_cast<char*>(pc); // ok: but writing through p is undefined

Conventionally we say that a cast that converts a const object to a nonconst type “casts away the const.” Once we have cast away the const of an object, the compiler will no longer prevent us from writing to that object. If the object was originally not a const, using a cast to obtain write access is legal. However, using a const_cast in order to write to a const object is undefined.

Only a const_cast may be used to change the constness of an expression. Trying to change whether an expression is const with any of the other forms of named cast is a compile-time error. Similarly, we cannot use a const_cast to change the type of an expression:

const char *cp;
// error: static_cast can't cast away const
char *q = static_cast<char*>(cp);
static_cast<string>(cp); // ok: converts string literal to string
const_cast<string>(cp);  // error: const_cast only changes constness

A const_cast is most useful in the context of overloaded functions, which we’ll describe in § 6.4 (p. 232).

reinterpret_cast

A reinterpret_cast generally performs a low-level reinterpretation of the bit pattern of its operands. As an example, given the following cast

int *ip;
char *pc = reinterpret_cast<char*>(ip);

we must never forget that the actual object addressed by pc is an int, not a character. Any use of pc that assumes it’s an ordinary character pointer is likely to fail at run time. For example:

string str(pc);

is likely to result in bizarre run-time behavior.

The use of pc to initialize str is a good example of why reinterpret_cast is dangerous. The problem is that types are changed, yet there are no warnings or errors from the compiler. When we initialized pc with the address of an int, there is no error or warning from the compiler because we explicitly said the conversion was okay. Any subsequent use of pc will assume that the value it holds is a char*. The compiler has no way of knowing that it actually holds a pointer to an int. Thus, the initialization of str with pc is absolutely correct—albeit in this case meaningless or worse! Tracking down the cause of this sort of problem can prove extremely difficult, especially if the cast of ip to pc occurs in a file separate from the one in which pc is used to initialize a string.


Image Warning

A reinterpret_cast is inherently machine dependent. Safely using reinterpret_cast requires completely understanding the types involved as well as the details of how the compiler implements the cast.


Old-Style Casts

In early versions of C++, an explicit cast took one of the following two forms:

type (expr); // function-style cast notation
(type) expr; // C-language-style cast notation

Depending on the types involved, an old-style cast has the same behavior as a const_cast, a static_cast, or a reinterpret_cast. When we use an old-style cast where a static_cast or a const_cast would be legal, the old-style cast does the same conversion as the respective named cast. If neither cast is legal, then an old-style cast performs a reinterpret_cast. For example:

char *pc = (char*) ip; // ip is a pointer to int

has the same effect as using a reinterpret_cast.


Image Warning

Old-style casts are less visible than are named casts. Because they are easily overlooked, it is more difficult to track down a rogue cast.



Exercises Section 4.11.3

Exercise 4.36: Assuming i is an int and d is a double write the expression i *= d so that it does integral, rather than floating-point, multiplication.

Exercise 4.37: Rewrite each of the following old-style casts to use a named cast:

int i;  double d;  const string *ps;  char *pc;  void *pv;

(a) pv = (void*)ps;

(b) i = int(*pc);

(c) pv = &d;

(d) pc = (char*) pv;

Exercise 4.38: Explain the following expression:

double slope = static_cast<double>(j/i);


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset