Menu

Conversión de tipos en C++: static_cast, conversiones implícitas y casts

Cómo funciona la conversión de tipos en C++: conversiones implícitas, la trampa de la división entera y los cuatro casts con nombre (static_cast, const_cast, reinterpret_cast, dynamic_cast), con los detalles que causan pérdida silenciosa de datos.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Qué es la conversión de tipos

Convertir tipos significa transformar un valor de un tipo a otro: convertir un double en un int, un char en su código numérico, o un puntero a la clase base en uno a la clase derivada. C++ hace algunas de estas conversiones por ti automáticamente, pero las que hace en silencio son justo donde se esconden los errores.

Hay dos sabores: conversiones implícitas que ocurren solas y casts explícitos que escribes tú. Ya viste un síntoma de esto en operadores: la división entera. El casting es la forma de tomar el control.

Conversiones implícitas

Cuando mezclas tipos numéricos en una expresión, C++ promueve el tipo "más pequeño" al "más grande" para que ambos lados coincidan. Normalmente esto hace lo que quieres.

El problema empieza cuando la conversión va en el sentido contrario: de un tipo más amplio a uno más estrecho. Eso es una conversión de estrechamiento, y puede perder datos en silencio.

De float a int se trunca hacia cero: no redondea, así que 3.99 se convierte en 3. Y meter 300 en un char produce un desbordamiento. Muchos compiladores avisan aquí; algunos no. Cuando de verdad quieras estrechar, dilo explícitamente con un cast para que el siguiente lector sepa que fue a propósito.

Cómo arreglar la trampa de la división entera

La razón más común para hacer un cast es la división. Cuando ambos operandos son enteros, / hace división entera y descarta el resto.

El arreglo es static_cast<double> sobre un operando antes de la división. Un error frecuente es static_cast<double>(got / total): llega demasiado tarde, porque got / total ya vale 0 cuando se ejecuta el cast, así que obtienes 0.0. Convierte un operando, no el resultado.

static_cast: tu cast por defecto

C++ te da cuatro casts con nombre. El que usarás el 95 % de las veces es static_cast<T>(value), que realiza conversiones bien definidas entre tipos relacionados: conversiones numéricas, de enum a int, de void* de vuelta a un puntero tipado, y subir o bajar por una jerarquía de clases cuando ya conoces el tipo.

Prefiere static_cast antes que el viejo cast estilo C (int)balance. Un cast estilo C intentará cualquier conversión para que el código compile, incluidas las peligrosas de más abajo, así que puede quitar const en silencio o reinterpretar bytes en bruto. static_cast solo permite conversiones que el compilador puede justificar de verdad, y el verboso static_cast<...> es trivial de buscar en una revisión de código.

// Evitar - cast estilo C, sin red de seguridad:
int dollars = (int) balance;

// Preferir - explícito, verificado, fácil de buscar:
int dollars = static_cast<int>(balance);

Los otros tres casts (úsalos con moderación)

Los casts restantes existen para trabajos concretos y muy específicos. Recurre a ellos solo cuando static_cast realmente no pueda hacerlo.

const_cast quita (o añade) const. Su único uso legítimo es llamar a una API estilo C que olvidó marcar un parámetro como const. Modificar a través de un const_cast un objeto que se declaró originalmente como const es comportamiento indefinido.

void legacyApi(char* msg);   // API antigua, no acepta const

const char* text = "hello";
legacyApi(const_cast<char*>(text));   // solo está bien si legacyApi no escribe en él

reinterpret_cast reinterpreta el patrón de bits en bruto, por ejemplo un puntero como una dirección entera. No realiza ninguna conversión y es tremendamente inseguro; casi siempre es señal de que deberías replantear el diseño.

dynamic_cast convierte de forma segura un puntero o referencia a la clase base hacia un tipo derivado en tiempo de ejecución, usando el tipo real del objeto. Requiere una base polimórfica (una clase con al menos una función virtual) y devuelve nullptr si el cast no aplica.

Si a hubiera apuntado a otro Animal, dynamic_cast<Dog*> devolvería nullptr y se ejecutaría la rama else, que es exactamente por lo que es más seguro que usar static_cast a ciegas para bajar por una jerarquía.

Errores comunes que evitar

  • Convertir el resultado en lugar de un operando. static_cast<double>(a / b) descarta primero tu fracción. Convierte a o b.
  • Suponer que de float a int se redondea. Se trunca: static_cast<int>(2.99) es 2. Para redondear usa std::round, std::lround, etc.
  • Recurrir a un cast estilo C. Oculta qué conversión ocurre. Usa static_cast y obtendrás un error de compilación cuando la conversión sea insegura en lugar de una sorpresa silenciosa.
  • Estrechar a un tipo demasiado pequeño. Convertir 300 a char o un long enorme a int envuelve o desborda. Elige un tipo destino lo bastante amplio para el rango.

Siguiente: If-Else

Ahora que puedes convertir y comparar valores con limpieza, el siguiente paso es tomar decisiones con ellos. La sentencia if-else ejecuta código diferente según si una condición es true: el cimiento de todo programa con ramificaciones.

Preguntas frecuentes

¿Cuál es la diferencia entre static_cast y un cast estilo C en C++?

Un cast estilo C como (int)x prueba cada conversión por turnos: puede convertirse silenciosamente en un peligroso reinterpret_cast o quitar el const. static_cast<int>(x) solo realiza conversiones relacionadas que el compilador puede verificar, así que el compilador rechaza los disparates. En C++ moderno, prefiere siempre static_cast antes que los casts estilo C; es más seguro y mucho más fácil de buscar con grep.

¿Cómo convierto un int a double en C++?

Usa static_cast<double>(x). Esto importa sobre todo con la división: 5 / 2 es división entera y da 2, pero static_cast<double>(5) / 2 da 2.5. Convierte uno de los operandos antes de que ocurra la división: convertir el resultado, static_cast<double>(5 / 2), llega demasiado tarde y sigue dando 2.0.

¿Por qué convertir un valor grande a un tipo más pequeño da un número incorrecto en C++?

Convertir a un tipo que no puede contener el valor es una conversión de estrechamiento. De float a int se trunca la parte fraccionaria (static_cast<int>(3.99) es 3), y un entero fuera de rango o se envuelve (sin signo) o queda definido por la implementación (con signo). El compilador normalmente no te detiene, así que convierte de forma deliberada y asegúrate de que el tipo destino sea lo bastante amplio.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR