static_cast
conversion
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).
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:
-
Derivedes un tipo de clase completo. -
Basees una clase base deDerived. - cv1 no es una calificación cv mayor que cv2 .
-
Basees una clase base virtual deDerived. -
Basees una clase base de una clase base virtual deDerived. -
No existe ninguna
conversión estándar
válida de "puntero a
Derived" a "puntero aBase".
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.
|
(desde C++11) |
|
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:
La conversión explícita se define de la siguiente manera:
|
(desde C++17) |
- conversión lvalue-a-rvalue
- conversión array-a-puntero
- conversión función-a-puntero
- conversión de puntero nulo
- conversión de puntero a miembro nulo
- conversión booleana
| (desde C++17) |
|
a)
Un valor de tipo
enumeración con ámbito
puede convertirse a un tipo entero o de punto flotante.
|
(desde C++11) |
- Si target-type tiene un tipo subyacente fijo, expression se convierte primero a ese tipo mediante promoción integral o conversión integral , si es necesario, y luego a target-type .
- Si target-type no tiene un tipo subyacente fijo, el valor de expression permanece sin cambios si el valor original está dentro del rango de los valores de la enumeración , de lo contrario el comportamiento es indefinido.
|
d)
Un prvalue de tipo punto flotante puede convertirse explícitamente a cualquier otro tipo punto flotante.
|
(desde C++23) |
Base
" puede convertirse explícitamente al tipo "puntero a
cv2
Derived
" si se cumplen todas las siguientes condiciones:
-
Derivedes un tipo de clase completo. -
Basees una clase base deDerived. - cv1 no es una calificación cv mayor que cv2 .
Derived
que contiene al objeto de tipo
Base
apuntado por
expression
.
-
Basees una clase base virtual deDerived. -
Basees una clase base de una clase base virtual deDerived. -
No existe una conversión estándar válida de "puntero a
Derived" a "puntero aBase".
Derived
, el comportamiento es indefinido.
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:
-
Derivedes un tipo de clase completo. -
Basees una clase base deDerived. - cv1 no es una calificación cv mayor que cv2 .
Base
.
Base
de tipo
T
" a "puntero a miembro de
Derived
de tipo
T
", el programa está mal formado.
Base
, el comportamiento es indefinido.
T
" si
T
es un tipo objeto y
cv1
no es una calificación-cv mayor que
cv2
.
|
(until C++17) |
|
(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) ;
|
(since C++11) |
- un prvalue en caso contrario.
- ↑ Este tipo de static_cast se utiliza para implementar la semántica de movimiento en std::move .
- ↑ 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
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]