Namespaces
Variants

static_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Convierte entre tipos usando una combinación de conversiones implícitas y definidas por el usuario.

Contenidos

Sintaxis

static_cast< target-type  >( expression  )

Devuelve un valor de tipo target-type .

Explicación

Solo las siguientes conversiones pueden realizarse con static_cast , excepto cuando dichas conversiones eliminen la constancia (o volatilidad).

1) Si expression es un lvalue de tipo " cv1 Base " y target-type es "referencia a cv2 Derived ", el resultado se refiere al objeto de tipo Derived que contiene a expression si se satisfacen todas las siguientes condiciones:
  • Derived es un tipo de clase completo.
  • Base es una clase base de Derived .
  • cv1 no es una calificación cv mayor que cv2 .
Si se satisface cualquiera de las siguientes condiciones, el programa está mal formado:
Si expresión no es realmente un subobjeto de clase base de un objeto de tipo Derived , el comportamiento es indefinido.
struct B {};
struct D : B { B b; };
D d;
B& br1 = d;
B& br2 = d.b;
static_cast<D&>(br1); // OK, lvalue que denota el objeto original "d"
static_cast<D&>(br2); // UB: el subobjeto "b" no es un subobjeto de clase base
2) Si target-type es "referencia a valor r a Derived " y expression es un xvalue de tipo "(posiblemente calificado con cv) Base " tal que Base es una clase base de Derived , el resultado y las restricciones de dicha conversión son los mismos que los de la conversión "lvalue de Base a referencia de Derived ".
3) Si target-type es un tipo de referencia a valor r y el tipo referenciado es compatible-con-referencia con el tipo de expression , static_cast convierte el valor de glvalue, class prvalue, o array prvalue (hasta C++17) cualquier lvalue (desde C++17) expression a xvalue que referencia al mismo objeto que la expresión, o a su subobjeto de clase base (dependiendo de target-type ). [1]
Si target-type es una base inaccesible o ambigua del tipo de expression , el programa está mal formado.
Si expression es un campo de bits lvalue, primero se convierte a prvalue del tipo subyacente.
(desde C++11)
4) Si target-type es (posiblemente calificado con cv) void , la conversión no tiene resultado. En este caso, expression es una expresión de valor descartado .
5) En caso contrario, expression puede ser convertida explícitamente a target-type si

la declaración target-type temp ( expression  ) ; es válida para alguna variable temporal inventada temp .

El efecto de dicha conversión explícita es el mismo que realizar la declaración e inicialización y luego usar temp como resultado de la conversión. La expression  se usa como un lvalue (hasta C++11) un glvalue (desde C++11) si y solo si la inicialización la usa como un lvalue (hasta C++11) un glvalue (desde C++11) .

(hasta C++17)

se satisface alguna de las siguientes condiciones:

  • target-type es un tipo agregado que tiene un primer elemento x y existe una secuencia de conversión implícita desde expression al tipo de x .
(desde C++20)

La conversión explícita se define de la siguiente manera:

  • Si target-type es un tipo referencia, el efecto es el mismo que realizar la declaración e inicialización target-type temp ( expression  ) ; para alguna variable temporal inventada temp y luego usar temp como resultado de la conversión.
  • En caso contrario, el objeto resultante se inicializa directamente desde expression .
(desde C++17)
6) De lo contrario, si la conversión de expression a target-type es una inversa de una secuencia de conversión estándar, y la secuencia de conversión no contiene ninguna de las siguientes conversiones, la conversión puede realizarse mediante static_cast :
(desde C++17)
Si un programa utiliza static_cast para realizar la inversa de una secuencia de conversión estándar mal formada, está mal formado.
7) En caso contrario, se aplican las conversiones de lvalue-a-rvalue, array-a-puntero y función-a-puntero a expression . Después de estas conversiones, solo las siguientes conversiones pueden ser realizadas por static_cast :
a) Un valor de tipo enumeración con ámbito puede convertirse a un tipo entero o de punto flotante.
  • Si target-type es (posiblemente calificado con cv) bool , el resultado es false si el valor original de expression es cero y true para todos los demás valores.
  • Si target-type es un tipo integral distinto de (posiblemente calificado con cv) bool , el valor permanece sin cambios si el valor original de expression puede ser representado por target-type . De lo contrario, el valor resultante no está especificado.
(hasta C++20)
  • Si target-type es un tipo integral, el resultado es el mismo que convertir al tipo subyacente de la enumeración y luego a target-type .
(desde C++20)
  • Si target-type es un tipo de punto flotante, el resultado es el mismo que convertir desde el valor original a target-type .
(desde C++11)
b) Un valor de tipo entero o de enumeración puede convertirse a cualquier tipo de enumeración completo.
c) Un valor de tipo punto flotante también puede convertirse a cualquier tipo de enumeración completo. El resultado es el mismo que convertir el valor original de expression primero al tipo subyacente de target-type , y luego a target-type mismo.
d) Un prvalue de tipo punto flotante puede convertirse explícitamente a cualquier otro tipo punto flotante.
  • Si el valor fuente de expression puede representarse exactamente en target-type , no cambia.
  • De lo contrario, si el valor fuente de expression está entre dos valores representables de target-type , el resultado de la conversión es una elección definida por la implementación de cualquiera de esos valores. [2]
  • De lo contrario, el comportamiento es indefinido.
(desde C++23)
e) Un rvalue (until C++11) Un prvalue (since C++11) de tipo "puntero a cv1 Base " puede convertirse explícitamente al tipo "puntero a cv2 Derived " si se cumplen todas las siguientes condiciones:
  • Derived es un tipo de clase completo.
  • Base es una clase base de Derived .
  • cv1 no es una calificación cv mayor que cv2 .
Si expression es un valor de puntero nulo , el resultado es un valor de puntero nulo de tipo target-type . De lo contrario, el resultado es un puntero al objeto de tipo Derived que contiene al objeto de tipo Base apuntado por expression .
Si se satisface cualquiera de las siguientes condiciones, el programa está mal formado:
  • Base es una clase base virtual de Derived .
  • Base es una clase base de una clase base virtual de Derived .
  • No existe una conversión estándar válida de "puntero a Derived " a "puntero a Base ".
Si expression no es un valor de puntero nulo y no apunta realmente a un subobjeto de clase base de un objeto de tipo Derived , el comportamiento es indefinido.
f) Un valor r (hasta C++11) Un prvalue (desde C++11) de tipo "puntero a miembro de Derived de tipo cv1 T " puede convertirse explícitamente al tipo "puntero a miembro de Base de tipo cv2 T " si se cumplen todas las siguientes condiciones:
  • Derived es un tipo de clase completo.
  • Base es una clase base de Derived .
  • cv1 no es una calificación cv mayor que cv2 .
Si expression es un valor de puntero a miembro nulo, el resultado es un valor de puntero a miembro nulo de tipo target-type . De lo contrario, el resultado es un puntero al miembro original (posiblemente indirecto) de la clase Base .
Si no existe una conversión estándar válida de "puntero a miembro de Base de tipo T " a "puntero a miembro de Derived de tipo T ", el programa está mal formado.
Si expression no es un valor de puntero a miembro nulo y el miembro que denota no es un miembro (posiblemente indirecto) de la clase Base , el comportamiento es indefinido.
g) Un rvalue (until C++11) Un prvalue (since C++11) de tipo "puntero a cv1 void " puede convertirse explícitamente al tipo "puntero a cv2 T " si T es un tipo objeto y cv1 no es una calificación-cv mayor que cv2 .
  • Si expresión es un valor de puntero nulo, el resultado es un valor de puntero nulo del tipo tipo-destino .
  • Si la expresión representa la dirección A de un byte en memoria y A satisface el requisito de alineación de T , entonces el valor del puntero resultante también representa A .
  • El resultado de cualquier otra conversión de puntero de este tipo no está especificado.
  • Si expresión es el resultado de una conversión previa de un objeto de tipo "puntero a cv3 T ", el resultado tiene el valor original.
(until C++17)
  • Si expresión representa la dirección A de un byte en memoria pero A no satisface el requisito de alineación de T , entonces el valor del puntero resultante no está especificado.
  • En caso contrario, si expresión apunta a un objeto a , y existe un objeto b de tipo T (ignorando la calificación-cv) que es interconvertible por puntero (ver abajo) con a , el resultado es un puntero a b .
  • En caso contrario, el valor del puntero no se modifica por la conversión.
(since C++17)

Como con todas las expresiones de conversión, el resultado es:

  • un lvalue si target-type es un tipo de referencia lvalue o una referencia rvalue a tipo función (desde C++11) ;
  • un xvalue si target-type es una referencia a rvalue a tipo objeto;
(since C++11)
  • un prvalue en caso contrario.
  1. Este tipo de static_cast se utiliza para implementar la semántica de movimiento en std::move .
  2. Si la aritmética IEEE está soportada, el redondeo por defecto es al más cercano.

Objetos intercambiables por puntero

Dos objetos a y b son pointer-interconvertible si:

  • son el mismo objeto, o
  • uno es un objeto union y el otro es un miembro de datos no estático de ese objeto, o
  • uno es un objeto de clase standard-layout y el otro es el primer miembro de datos no estático de ese objeto o cualquier subobjeto de clase base de ese objeto, o
  • existe un objeto c tal que a y c son pointer-interconvertible, y c y b son pointer-interconvertible.
union U { int a; double b; } u;
void* x = &u;                        // el valor de x es "puntero a u"
double* y = static_cast<double*>(x); // el valor de y es "puntero a u.b"
char* z = static_cast<char*>(x);     // el valor de z es "puntero a u"

Notas

Conversiones de base a derivado ( downcasts ) usando static_cast no realizan comprobaciones en tiempo de ejecución para garantizar que el tipo dinámico del objeto apuntado/referenciado es Derived , y solo pueden usarse de forma segura si esta precondición está garantizada por otros medios, como al implementar polimorfismo estático . El downcast seguro puede realizarse con dynamic_cast .

static_cast también puede utilizarse para eliminar la ambigüedad de sobrecargas de funciones realizando una conversión de función a puntero a un tipo específico, como en

std::for_each(files.begin(), files.end(),
              static_cast<std::ostream&(*)(std::ostream&)>(std::flush));

Palabras clave

static_cast

Ejemplo

#include <iostream>
#include <vector>
struct B
{
    int m = 42;
    const char* hello() const
    {
        return "Hello world, this is B!\n";
    }
};
struct D : B
{
    const char* hello() const
    {
        return "Hello world, this is D!\n";
    }
};
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
int main()
{
    // 1. conversión descendente estática
    D d;
    B& br = d; // conversión ascendente mediante conversión implícita
    std::cout << "1) " << br.hello();
    D& another_d = static_cast<D&>(br); // conversión descendente
    std::cout << "1) " << another_d.hello();
    // 3. lvalue a xvalue
    std::vector<int> v0{1, 2, 3};
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v0);
    std::cout << "3) después del movimiento, v0.size() = " << v0.size() << '\n';
    // 4. expresión de valor descartado
    static_cast<void>(v2.size());
    // 5. conversión de inicialización
    int n = static_cast<int>(3.14);
    std::cout << "5) n = " << n << '\n';
    std::vector<int> v = static_cast<std::vector<int>>(10);
    std::cout << "5) v.size() = " << v.size() << '\n';
    // 6. inverso de conversión implícita
    void* nv = &n;
    int* ni = static_cast<int*>(nv);
    std::cout << "6) *ni = " << *ni << '\n';
    // 7a. enumeración con ámbito a int
    E e = E::TWO;
    int two = static_cast<int>(e);
    std::cout << "7a) " << two << '\n';
    // 7b. int a enumeración, enumeración a otra enumeración
    E e2 = static_cast<E>(two);
    [[maybe_unused]]
    EU eu = static_cast<EU>(e2);
    // 7f. puntero a miembro conversión ascendente
    int D::*pm = &D::m;
    std::cout << "7f) " << br.*static_cast<int B::*>(pm) << '\n';
    // 7g. void* a cualquier puntero de objeto
    void* voidp = &e;
    [[maybe_unused]]
    std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}

Salida:

1) Hola mundo, ¡este es B!
1) Hola mundo, ¡este es D!
3) después de mover, v0.size() = 0
5) n = 3
5) v.size() = 10
6) *ni = 3
7a) 2
7f) 42

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 137 C++98 la constancia y volatilidad de
punteros a void podían eliminarse mediante cast
las calificaciones cv no pueden
eliminarse en tales casos
CWG 427 C++98 el downcast podría ser ambiguo con la inicialización directa selecciona downcast en este caso
CWG 439 C++98 al convertir un "puntero a objeto" a "puntero a
void " y luego de vuelta a sí mismo, solo podía preservar su
valor si el tipo resultante tenía la misma calificación cv
la calificación cv
puede ser diferente
CWG 1094 C++98 la conversión de valores de punto flotante
a valores de enumeración no estaba especificada
especificada
CWG 1320 C++11 la conversión de valores de enumeración con ámbito
a bool no estaba especificada
especificada
CWG 1412 C++98 el resultado de la conversión de
"puntero a
void " a "puntero a objeto" no estaba claro
aclarado
CWG 1447 C++11 la conversión de campos de bits a referencias a rvalue
no estaba especificada (no se pueden enlazar referencias a campos de bits)
especificada
CWG 1766 C++98 la conversión de valores enteros o de enumeración a valores de enumeración
producía resultado no especificado si expresión está fuera de rango
el comportamiento es
indefinido en este caso
CWG 1832 C++98 la conversión de valores enteros o de enumeración a
valores de enumeración permitía que tipo-destino estuviera incompleto
no permitido
CWG 2224 C++98 la conversión de un miembro de tipo clase base a
su objeto completo de tipo clase derivada era válida
el comportamiento es
indefinido en este caso
CWG 2254 C++11 un objeto de clase de diseño estándar sin miembros de datos
era interconvertible por puntero a su primera clase base
es interconvertible por puntero
a cualquiera de sus clases base
CWG 2284 C++11 un objeto unión de diseño no estándar y un miembro de datos no estático
de ese objeto no eran interconvertibles por puntero
lo son
CWG 2310 C++98 para conversiones de puntero base-a-derivada y
conversiones de puntero-a-miembro derivada-a-base,
el tipo clase derivada podía estar incompleto
debe estar completo
CWG 2338 C++11 la conversión a tipos enumeración con tipo subyacente fijo
resultaba en comportamiento indefinido si expresión está fuera de rango
convertir al tipo subyacente
primero (sin comportamiento indefinido)
CWG 2499 C++11 una clase de diseño estándar podría tener una clase base no interconvertible por puntero,
aunque todos los subobjetos base tengan la misma dirección
no tiene
CWG 2718 C++98 para conversiones de referencia base-a-derivada,
el tipo clase derivada podía estar incompleto
debe estar completo
CWG 2882 C++98 no estaba claro si static_cast < void > ( expr ) intenta
formar una secuencia de conversión implícita de expr a void
no hay intento en este caso

Referencias

  • Estándar C++23 (ISO/IEC 14882:2024):
  • 7.6.1.9 Conversión estática [expr.static.cast]
  • Estándar C++20 (ISO/IEC 14882:2020):
  • 7.6.1.8 Conversión estática [expr.static.cast]
  • Estándar C++17 (ISO/IEC 14882:2017):
  • 8.2.9 Conversión estática [expr.static.cast]
  • Estándar C++14 (ISO/IEC 14882:2014):
  • 5.2.9 Conversión estática [expr.static.cast]
  • Estándar C++11 (ISO/IEC 14882:2011):
  • 5.2.9 Conversión estática [expr.static.cast]
  • Estándar C++98 (ISO/IEC 14882:1998):
  • 5.2.9 Static cast [expr.static.cast]
  • Estándar C++03 (ISO/IEC 14882:2003):
  • 5.2.9 Static cast [expr.static.cast]

Véase también