Namespaces
Variants

Explicit type 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 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.

1) Conversión explícita de tipo (notación de cast), también llamada C-style cast .
2-7) Conversión explícita de tipo (notación funcional), también llamada function-style cast .
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

1) Cuando se encuentra el cast estilo C, el compilador intenta interpretarlo como las siguientes expresiones de conversión, en este orden:
a) const_cast < type-id  > ( unary-expression  ) ;
b) 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;
c) a static_cast (con extensiones) seguido de const_cast ;
d) reinterpret_cast < type-id  > ( unary-expression  ) ;
e) a reinterpret_cast seguido de const_cast .
Se selecciona la primera opción que satisface los requisitos del operador de conversión respectivo, incluso si está mal formada (ver ejemplo). Si se utiliza un static_cast seguido por un const_cast y la conversión puede interpretarse de más de una manera como tal, la conversión está mal formada.
Además, las conversiones estilo C pueden convertir desde, hacia y entre punteros a tipos de clase incompletos. Si tanto type-id como el tipo de unary-expression son punteros a tipos de clase incompletos, no está especificado si se selecciona static_cast o reinterpret_cast .
2-7) Una conversión de estilo función especifica un tipo ( simple-type-specifier  o identifier  (desde C++11) ) y un inicializador (las partes restantes), construye un valor del tipo destino T , que se determina a partir del tipo especificado y el inicializador (desde C++17) :

T es el tipo especificado.

(hasta C++17)

T se determina de la siguiente manera:

(desde C++23)
  • En caso contrario, T es el tipo especificado.
(desde C++17)
El resultado de la conversión se determina de la siguiente manera:
  • 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 T es (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.
  • Si el inicializador no es ( ) , el programa está mal formado.
(hasta C++11)
  • Si el inicializador no es ( ) o { } después de la expansión de paquete (si la hay), el programa está mal formado.
(desde C++11)
  • De lo contrario, si T es un tipo referencia, la conversión de estilo función tiene el mismo efecto que inicializar directamente una variable inventada t de tipo T a partir del inicializador especificado, y el resultado es la t inicializada.
  • El resultado es un lvalue.
(hasta C++11)
  • Si T es un tipo referencia a lvalue o una referencia a rvalue de tipo función, el resultado es un lvalue.
  • De lo contrario, el resultado es un xvalue.
(desde C++11)
  • De lo contrario, el resultado es un rvalue (hasta C++11) un prvalue (desde C++11) de tipo T que 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