Explicit type conversion
Convierte entre tipos usando una combinación de conversiones explícitas e implícitas.
Contenidos |
Sintaxis
(
type-id
)
unary-expression
|
(1) | ||||||||
simple-type-specifier
(
expression-list
(opcional)
)
simple-type-specifier
(
initializer-list
(opcional)
)
|
(2) |
(hasta C++11)
(desde C++11) |
|||||||
simple-type-specifier
{
initializer-list
(opcional)
}
|
(3) | (desde C++11) | |||||||
simple-type-specifier
{
designated-initializer-list
}
|
(4) | (desde C++20) | |||||||
typename
identifier
(
initializer-list
(opcional)
)
|
(5) | (desde C++11) | |||||||
typename
identifier
{
initializer-list
(opcional)
}
|
(6) | (desde C++11) | |||||||
typename
identifier
{
designated-initializer-list
}
|
(7) | (desde C++20) | |||||||
Convierte explícitamente cualquier cantidad de valores a un valor del tipo de destino.
| type-id | - | un type-id |
| unary-expression | - | una expresión unaria (cuyo operador principal no tiene una precedencia mayor que la del cast estilo C) |
| simple-type-specifier | - | un simple type specifier |
| expression-list | - | una lista separada por comas de expresiones (excepto comma expressions sin paréntesis) |
| initializer-list | - | una lista separada por comas de initializer clauses |
| designated-initializer-list | - | una lista separada por comas de designated initializer clauses |
| identifier | - | un identificador (posiblemente calificado) (incluyendo template identifiers ) |
Explicación
const_cast
<
type-id
>
(
unary-expression
)
;
static_cast
<
type-id
>
(
unary-expression
)
, con extensiones: se permite adicionalmente convertir un puntero o referencia a una
clase derivada
a puntero o referencia de una clase base no ambigua (y viceversa) incluso si la clase base es
inaccesible
(es decir, esta conversión ignora el especificador de herencia privada). Lo mismo aplica para convertir
puntero a miembro
a puntero a miembro de una base no virtual no ambigua;
reinterpret_cast
<
type-id
>
(
unary-expression
)
;
T
, que se determina a partir del tipo especificado
y el inicializador
(desde C++17)
:
|
|
(hasta C++17) | ||
|
|
(desde C++17) |
- Si la conversión de estilo función es de sintaxis (2) , y hay exactamente una expresión entre paréntesis, esta conversión es equivalente a la conversión de estilo C correspondiente.
-
De lo contrario, si
Tes (posiblemente calificado con cv) void , el resultado es un rvalue (hasta C++11) un prvalue (desde C++11) de tipo void que no realiza ninguna inicialización.
|
(hasta C++11) |
|
(desde C++11) |
-
De lo contrario, si
Tes un tipo referencia, la conversión de estilo función tiene el mismo efecto que inicializar directamente una variable inventada t de tipoTa partir del inicializador especificado, y el resultado es la t inicializada.
|
(hasta C++11) |
|
(desde C++11) |
-
De lo contrario, el resultado es
un rvalue
(hasta C++11)
un prvalue
(desde C++11)
de tipo
Tque designa un temporal (hasta C++17) cuyo objeto resultado es (desde C++17) inicializado directamente con el inicializador especificado.
Resolución de Ambigüedad
Declaración ambigua
En caso de ambigüedad entre una sentencia de expresión con una expresión de conversión de estilo función como su subexpresión más a la izquierda y una sentencia de declaración, la ambigüedad se resuelve tratándola como una declaración. Esta desambiguación es puramente sintáctica: no considera el significado de los nombres que aparecen en la sentencia, excepto si son nombres de tipo:
struct M {}; struct L { L(M&); }; M n; void f() { M(m); // declaración, equivalente a M m; L(n); // declaración incorrecta, equivalente a L n; L(l)(m); // sigue siendo una declaración, equivalente a L l((m)); }
|
Sin embargo, si el declarador más externo en la declaración ambigua tiene un tipo de retorno final , la declaración solo se tratará como una declaración si el tipo de retorno final comienza con auto : struct M; struct S { S* operator()(); int N; int M; void mem(S s) { auto(s)()->M; // expresión (S::M oculta ::M), inválido antes de C++23 } }; void f(S s) { { auto(s)()->N; // expresión, inválido antes de C++23 auto(s)()->M; // declaración de función, equivalente a M s(); } { S(s)()->N; // expresión S(s)()->M; // expresión } } |
(desde C++11) |
Parámetro de función ambiguo
La ambigüedad anterior también puede ocurrir en el contexto de una declaración. En ese contexto, la elección está entre una declaración de objeto con un cast de estilo función como inicializador y una declaración que involucra un declarador de función con un conjunto redundante de paréntesis alrededor de un nombre de parámetro. La resolución también es considerar cualquier construcción, como la posible declaración de parámetro, que posiblemente podría ser una declaración como una declaración:
struct S { S(int); }; void foo(double a) { S w(int(a)); // declaración de función: tiene un parámetro `a` de tipo int S x(int()); // declaración de función: tiene un parámetro sin nombre de tipo int(*)() // que se ajusta desde int() // Formas de evitar ambigüedad: S y((int(a))); // declaración de objeto: paréntesis adicional S y((int)a); // declaración de objeto: conversión estilo C S z = int(a); // declaración de objeto: no hay ambigüedad con esta sintaxis }
|
Sin embargo, si el declarador más externo en la declaración de parámetro ambigua tiene un tipo de retorno final , la ambigüedad solo se resolverá tratándolo como una declaración si comienza con auto : typedef struct BB { int C[2]; } *B, C; void foo() { S a(B()->C); // declaración de objeto: B()->C no puede declarar un parámetro S b(auto()->C); // declaración de función: tiene un parámetro sin nombre de tipo C(*)() // que se ajusta desde C() } |
(desde C++11) |
Identificador de tipo ambiguo
Una ambigüedad puede surgir de la similitud entre un cast de estilo función y un type-id . La resolución es que cualquier construcción que posiblemente pueda ser un type-id en su contexto sintáctico será considerada un type-id:
// `int()` y `int(unsigned(a))` pueden ser analizados como type-id: // `int()` representa una función que devuelve int // y no toma argumentos // `int(unsigned(a))` representa una función que devuelve int // y toma un argumento de tipo unsigned void foo(signed char a) { sizeof(int()); // type-id (mal formado) sizeof(int(a)); // expresión sizeof(int(unsigned(a))); // type-id (mal formado) (int()) + 1; // type-id (mal formado) (int(a)) + 1; // expresión (int(unsigned(a))) + 1; // type-id (mal formado) }
|
Sin embargo, si el abstract-declarator más externo en el type-id ambiguo tiene un trailing return type , la ambigüedad solo se resolverá tratándolo como un type-id si comienza con auto : typedef struct BB { int C[2]; } *B, C; void foo() { sizeof(B()->C[1]); // OK, sizeof(expression) sizeof(auto()->C[1]); // error: sizeof of a function returning an array } |
(desde C++11) |
Notas
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_auto_cast
|
202110L
|
(C++23) | auto ( x ) y auto { x } |
Ejemplo
#include <cassert> #include <iostream> double f = 3.14; unsigned int n1 = (unsigned int)f; // conversión estilo C unsigned int n2 = unsigned(f); // conversión estilo función class C1; class C2; C2* foo(C1* p) { return (C2*)p; // convierte tipo incompleto a tipo incompleto } void cpp23_decay_copy_demo() { auto inc_print = [](int& x, const int& y) { ++x; std::cout << "x:" << x << ", y:" << y << '\n'; }; int p{1}; inc_print(p, p); // imprime x:2 y:2, porque el parámetro y aquí es un alias de p int q{1}; inc_print(q, auto{q}); // imprime x:2 y:1, auto{q} (C++23) convierte a prvalue, // por lo que el parámetro y es una copia de q (no un alias de q) } // En este ejemplo, la conversión estilo C se interpreta como static_cast // aunque funcionaría como reinterpret_cast struct A {}; struct I1 : A {}; struct I2 : A {}; struct D : I1, I2 {}; int main() { D* d = nullptr; // A* a = (A*)d; // error en tiempo de compilación A* a = reinterpret_cast<A*>(d); // esto compila assert(a == nullptr); cpp23_decay_copy_demo(); }
Salida:
x:2 y:2 x:2 y:1
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 1223
( P2915R0 ) |
C++11 | la adición del tipo de retorno final introdujo más ambigüedades | las resuelve |
| CWG 1893 | C++11 | la conversión estilo función no consideraba expansiones de paquetes | las considera |
| CWG 2351 | C++11 | void { } estaba mal formado | se hizo bien formado |
| CWG 2620 | C++98 |
la resolución de parámetros de función ambiguos
podría ser malinterpretada |
se mejoró la redacción |
| CWG 2828 | C++98 |
una conversión estilo C estaba mal formada si existían múltiples interpretaciones
de un static_cast seguido por un const_cast , independientemente de si estas conversiones se usaban realmente |
solo considera las
conversiones posiblemente utilizadas |
| CWG 2894 | C++98 | las conversiones estilo función podían crear rvalues de referencia | solo pueden crear lvalues de referencia |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
- 7.6.1.4 Conversión explícita de tipo (notación funcional) [expr.type.conv]
-
- 7.6.3 Conversión explícita de tipo (notación de cast) [expr.cast]
- Estándar C++20 (ISO/IEC 14882:2020):
-
- 7.6.1.4 Conversión explícita de tipo (notación funcional) [expr.type.conv]
-
- 7.6.3 Conversión explícita de tipo (notación de cast) [expr.cast]
- Estándar C++17 (ISO/IEC 14882:2017):
-
- 8.2.3 Conversión explícita de tipo (notación funcional) [expr.type.conv]
-
- 8.4 Conversión explícita de tipo (notación de cast) [expr.cast]
- Estándar C++14 (ISO/IEC 14882:2014):
-
- 5.2.3 Conversión explícita de tipo (notación funcional) [expr.type.conv]
-
- 5.4 Conversión explícita de tipo (notación de cast) [expr.cast]
- Estándar C++11 (ISO/IEC 14882:2011):
-
- 5.2.3 Conversión explícita de tipo (notación funcional) [expr.type.conv]
-
- 5.4 Conversión explícita de tipo (notación de cast) [expr.cast]
- Estándar C++03 (ISO/IEC 14882:2003):
-
- 5.2.3 Conversión explícita de tipo (notación funcional) [expr.type.conv]
-
- 5.4 Conversión explícita de tipo (notación de cast) [expr.cast]
- Estándar C++98 (ISO/IEC 14882:1998):
-
- 5.2.3 Conversión explícita de tipo (notación funcional) [expr.type.conv]
-
- 5.4 Conversión explícita de tipo (notación de cast) [expr.cast]
Véase también
const_cast
conversión
|
añade o elimina const |
static_cast
conversión
|
realiza conversiones básicas |
dynamic_cast
conversión
|
realiza conversiones polimórficas verificadas |
reinterpret_cast
conversión
|
realiza conversiones generales de bajo nivel |
| conversiones estándar | conversiones implícitas de un tipo a otro |
|
documentación de C
para
operador de conversión
|
|