# Conversión de tipos en C++

Al igual que ocurre en C, el compilador de C++ soporta convertir variables de un tipo en otro diferente. Sin embargo, aunque C++ permite indicar estas conversiones mediante la misma sintaxis que C, lo recomendado es utilizar los operadores específicos de C++.

# Conversión de tipos en estilo C

En C el compilador es capaz de hacer ciertas conversiones de tipos de forma automática —o implícita—. Por ejemplo, ir de `char` a `int` o de este a `float` es algo que hace el compilador sin que nos demos cuenta:

```cpp
int i = 10;  
float d = i;        /* correcto */
```

Sin embargo, hay conversiones que no son válidas:

```cpp
int* i = NULL;  
float* d = i;       /* conversión inválida de 'int*' a 'float*' */
```

cuyo comportamiento por defecto no es el deseado:

```cpp
int a = 10;  
int b = 7;  
float c = a / b;    /* c = 1.0 y no 1.4, como podría esperarse */
```

Para esos casos el lenguaje nos permite forzar la conversión de tipos, utilizando una expresión de *typecast* de la forma `(type)object` —o `type(object)`— indicando así que queremos convertir `object` al tipo especificado por `type`. Por ejemplo:

```cpp
int a = 10;  
int b = 7;  
float c = (float)a / (float)b;  /* c = 1.4 */
```

En C++ se puede utilizar la misma expresión de *typecast* que en C, aunque no es lo más aconsejable. En su lugar, C++ ofrece diversos operadores de *typecast* cuyo uso es más adecuado y menos peligroso que la conversión de tipos *estilo C*.

# static\_cast

El operador:

```cpp
static_cast(object)
```

es siempre el primer tipo de conversión que debemos intentar utilizar. Permite invocar conversiones implícitas entre tipos —es decir, esas conversiones automáticas del compilador que mencionamos al principio—.

Por ejemplo, la conversión implícita de `int` a `float`:

```cpp
int a = 10;  
float d = a;
```

también se puede hacer así:

```cpp
int a = 10;  
float d = static_cast<float>(a);    // correcto
```

quizás para hacer correctamente una división de enteros en coma flotante, evitando perder los decimales

```cpp
int a = 10;  
int b = 7;  
float c = static_cast<float>(a) / static_cast<float>(b); // c = 1.4  
// float c = a / b                                       // c = 1.0
```

También permite la conversión de cualquier tipo de puntero a `void*`:

```cpp
int* pa = nullptr;  
void* pb = static_cast<void*>(pa);  // correcto
```

que es equivalente a hacer directamente `void* pb = pa`, ya que la conversión de cualquier puntero a `void*` se hace de forma implícita. Sin embargo, también admite la conversión inversa, que no es una conversión implícita:

```cpp
void* pa = nullptr;  
char* pb = static_cast<char*>(pa);  // correcto
// char* pb = pa;                   // ¡error!
```

Por ejemplo, podríamos reservar 10 caracteres con `malloc()` así:

```cpp
char* c = static_cast<char*>(malloc(10 * sizeof(char)));
```

ya que la conversión del puntero `void*` que retorna `malloc()` a `char*` necesita un *typecast*.

En el caso de objetos, `static_cast` llama a los operadores de conversión explícitos definidos en las clases:

```cpp
class Foo  
{  
    // ...
    
    // Definición del operador de conversión de objetos Foo a const char*
    operator const char*()
    {  
        // ...  
    }  
};

Foo foo;

char* c = static_cast<char*>(foo);  // correcto
```

En ejemplo anterior, tanto si se recurre al uso de `static_cast` como si se hace la conversión de forma implícita: `char* c = foo`; el método `operator const char*()` de la clase `Foo` es llamado para realizar la conversión.

`static_cast` también convierte de clases bases a derivadas en una jerarquía de clases. La conversión inversa, de clases derivadas a clase base es automática, siempre que no haya polimorfismo. Es decir, siempre que la clase base no tenga algún método virtual:

```cpp
class Base  
{  
    // ...  
};

class Derived: public Base  
{  
    // ...  
};

Derived* derived = new Derive;

// Conversión implícita de puntero a Derived a puntero a Base.
Base* base = derived;

// Recuperar el puntero a Derived a partir del puntero a Base.
Derived* derived_de_nuevo = static_cast<Derived*>(base);
```

Hay que tener en cuenta que las conversiones `static_cast` se resuelven siempre en **tiempo de compilación**, por lo que no se comprueba si el tipo al que se convierte coincide con el tipo real del objeto. El estándar indica que queda indefinido lo que pueda pasar si se convierte de un tipo base a uno derivado cuando este último no es el tipo real del objeto.

Es decir, el resultado del siguiente ejemplo queda indefinido:

Por ejemplo:

```cpp
class Base  
{  
    // ...  
};

class Derived: public Base  
{  
    // ...  
};

Base* base = new Base;

Derived* derived = static_cast<Derived*>(base); // ¡indefinido!
```

porque intentamos obtener un puntero a `Derived` para un objeto creado directamente como `Base` y el operador `static_cast` no hace ninguna comprobación para determinar si el el puntero `base` realmente apunta a un objeto creado al instanciar la clase `Derived`.

# dynamic\_cast

El operador:

```cpp
dynamic_cast(object)
```

se utiliza exclusivamente para manejar el polimorfismo, ya que permite convertir un puntero o referencia de un tipo polimórfico —esto es, una clase con algún método virtual— a cualquier otro tipo. Eso no solo permite convertir de clases base a derivadas, sino también desplazarnos lateralmente e incluso movernos a una cadena de herencia diferente dentro de una misma jerarquía de clases.

```cpp
class Base  
{  
    // ...

    // Declarar el destructor virtual para que la clase sea polimórfica.
    virtual ~Base() {}
};

class Derived: public Base  
{  
    // ...  
};

Derived* derived = new Derive;  

// Conversión implícita de puntero a objeto de clase derivada a puntero
// a objeto de su clase base.
Base* base = derived;

// Recuperar el puntero a `Derived` a partir del puntero a `Base` usando
// `dynamic_cast`.
Derived* derived_de_nuevo = dynamic_cast<Derived*>(base);
```

`dynamic_cast` busca en **tiempo de ejecución** el objeto del tipo deseado en la jerarquía del objeto, devolviéndolo en caso de encontrarlo. Si los tipos no son compatibles —por ejemplo, si el objeto no fue creado originalmente con el tipo o con un tipo derivado del tipo indicado— `dynamic_cast` devuelve `nullptr`, si se lo usó con un puntero, o lanza una excepción `std::bad_cast`, si se lo usó con una referencia.

```cpp
class Base  
{  
    // ...
    
    // Declarar el destructor virtual para que la clase sea polimórfica.
    virtual ~Base() {}
};

class Derived: public Base  
{  
    // ...  
};

Base* base = new Base;

// Se obtiene `nullptr` al intentar obtener un puntero `Derived` para el objeto
// creado como `Base` 
Derived* derived = dynamic_cast<Derived*>(base); // = nullptr ¡error!
```

# const\_cast

El operador:

```cpp
const_cast(object)
```

se usa exclusivamente para eliminar o añadir `const` a una variable, ya que esto es algo que no pueden hacer los otros operadores de *typecast*.

Añadir `const` a un tipo es una conversión implícita. Es decir:

```cpp
int a = 10;  
const int b = const_cast<const int>(a); // correcto
```

es equivalente a:

```cpp
int a = 10;  
const int b = a;
```

pero quitar `const` no lo es:

```cpp
const int a = 10;  
int b = const_cast<int>(a); // correcto
// int b = a;               // ¡error!
```

Es importante destacar que su uso queda indefinido si la variable original realmente es constante. Por ejemplo, algunos compiladores optimizan las constantes reemplazándolas, allí dónde son utilizadas, directamente por el valor asignado. En ese caso, intentar modificar la variable tiene un resultado indefinido.

# reinterpret\_cast

El operador:

```cpp
reinterpret_cast(object)
```

instruye al compilador para que una expresión de un tipo sea tratada sin más como de un tipo diferente. No se genera código para llevar acabo la conversión de los datos y, por tanto, es el más peligroso de los operadores de *typecast*.

Se utiliza para convertir punteros de un tipo a otro de forma arbitraria. Por ejemplo, si se recibe un flujo de bytes como un `char*` pero dichos bytes realmente son una secuencia de enteros, con `reinterpret_cast` se puede convertir el puntero `char*` en `int*` para recuperar fácilmente cada uno de los números de la secuencia.

También se puede utilizar para convertir un puntero en un entero para manipular una dirección directamente. Por ejemplo, así podemos obtener la dirección de `c` en la memoria como un entero almacenado en la varible `p`:

```cpp
char* c = new char[15];  
uintptr_t p = reinterpret_cast<uintptr_t>(c);
```

La única garantía ofrecida por el estándar de C++ es que si se hace un `reinterpret_cast` y posteriormente se realiza otro para volver al tipo original, se obtiene el mismo resultado, siempre que el tipo intermedio tenga el tamaño suficiente para que no se pierda información.

# Conversión estilo C

Si en C++ se indica una conversión de *estilo C* —usando la sintaxis tradicional `(type)object` o `type(object)`— el efecto será el mismo que la primera conversión de la siguiente lista que tenga éxito:

1. **const\_cast**.
    
2. **static\_cast**.
    
3. **static\_cast** y después **const\_cast**.
    
4. **reinterpret\_cast**
    
5. **reinterpret\_cast** y después **const\_cast**.
    

Usar en C++ *typecasts* *estilo C* es peligroso porque pueden convertirse en un `reinterpret_cast` sin pretenderlo. Si hace falta este tipo de conversión, es preferible indicarlo explícitamente en el código usando el operador `reinterpret_cast`. Así, en caso de problemas, es más fácil buscar en el editor dónde pueden estar ocurriendo conversiones que pueden ser problemáticas.

Además, la conversión *estilo C* ignora el control de acceso de las clases —*protected* o *private*— por lo que este tipo de conversión permite hacer operaciones que con los operadores de C++ no se puede. Por ejemplo, en el siguiente caso la compilación termina con un error:

```cpp
class Base  
{  
    // ...  
};

class Derived: protected Base  
{  
    // ...  
};

Derived* derived = new Derived;  
Base* base = static_cast<Base*>(derived);   // ¡error!
```

ya que la clase `Base` es una clase base protegida de `Derived`. Sin embargo el ejemplo compila sin problemas usando tanto `reinterpret_cast` como un *typecast estilo C*:

```cpp
class Base  
{  
    // ...  
};

class Derived: protected Base  
{  
    // ...  
};

Derived* derived = new Derived;  
Base* base = reinterpret_cast<Base*>(derived);  // correcto  
//Base* base = (Base*)derived;                  // correcto
```

# Referencias

* [When should static\_cast, dynamic\_cast and reinterpret\_cast be used?](http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-and-reinterpret-cast-be-used) — Stack Overflow.
    
* [static\_cast restricts access to public member function?](http://stackoverflow.com/questions/8548667/static-cast-restricts-access-to-public-member-function) — Stack Overflow.
