Buscar..


Introducción

Una expresión puede ser convertido o fundido para escribir explícitamente T usando dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> , o const_cast<T> , dependiendo de qué tipo de molde que se pretende.

C ++ también admite la notación de conversión de estilo de función, T(expr) y la notación de conversión de estilo de C, (T)expr .

Sintaxis

  • especificador de tipo simple ( )
  • especificador de tipo simple ( expresión-lista )
  • especificador de tipo simple braced-init-list
  • typename-specifier ( )
  • typename-specifier ( expresión-lista )
  • nombre de archivo-especificador braced- init-list
  • dynamic_cast < type-id > ( expresión )
  • static_cast < type-id > ( expresión )
  • reinterpret_cast < type-id > ( expresión )
  • const_cast < type-id > ( expresión )
  • ( type-id ) expresión-cast

Observaciones

Las seis notaciones emitidas tienen una cosa en común:

  • La dynamic_cast<Derived&>(base) a un tipo de referencia dynamic_cast<Derived&>(base) , como en dynamic_cast<Derived&>(base) , produce un lvalue. Por lo tanto, cuando desea hacer algo con el mismo objeto pero tratarlo como un tipo diferente, se convertiría a un tipo de referencia de valor l.
  • La conversión a un tipo de referencia de rvalue, como en static_cast<string&&>(s) , produce un rvalue.
  • La conversión a un tipo que no es de referencia, como en (int)x , produce un prvalue, que puede considerarse como una copia del valor que se está emitiendo, pero con un tipo diferente del original.

La palabra clave reinterpret_cast es responsable de realizar dos tipos diferentes de conversiones "inseguras":

La palabra clave static_cast puede realizar una variedad de diferentes conversiones:

  • Base a conversiones derivadas

  • Cualquier conversión que se pueda realizar mediante una inicialización directa, incluidas las conversiones implícitas y las conversiones que llaman a un constructor explícito o una función de conversión. Vea aquí y aquí para más detalles.

  • Para void , lo que descarta el valor de la expresión.

    // on some compilers, suppresses warning about x being unused
    static_cast<void>(x);
    
  • Entre los tipos aritméticos y de enumeración, y entre diferentes tipos de enumeración. Ver las conversiones de enumeración

  • Del puntero al miembro de la clase derivada, al puntero del miembro de la clase base. Los tipos apuntados deben coincidir. Ver conversión derivada a base para punteros a miembros

  • void* a T* .

C ++ 11

Base a conversión derivada

Un puntero a la clase base se puede convertir en un puntero a la clase derivada usando static_cast . static_cast no realiza ninguna comprobación en tiempo de ejecución y puede provocar un comportamiento indefinido cuando el puntero no apunta al tipo deseado.

struct Base {};
struct Derived : Base {};
Derived d;
Base* p1 = &d;
Derived* p2 = p1;                        // error; cast required
Derived* p3 = static_cast<Derived*>(p1); // OK; p2 now points to Derived object
Base b;
Base* p4 = &b;
Derived* p5 = static_cast<Derived*>(p4); // undefined behaviour since p4 does not
                                         // point to a Derived object

Del mismo modo, una referencia a la clase base se puede convertir en una referencia a la clase derivada usando static_cast .

struct Base {};
struct Derived : Base {};
Derived d;
Base& r1 = d;
Derived& r2 = r1;                        // error; cast required
Derived& r3 = static_cast<Derived&>(r1); // OK; r3 now refers to Derived object

Si el tipo de fuente es polimórfico, dynamic_cast se puede usar para realizar una conversión de base a derivada. Realiza una verificación en tiempo de ejecución y la falla es recuperable en lugar de producir un comportamiento indefinido. En el caso del puntero, se devuelve un puntero nulo en caso de error. En el caso de referencia, se produce una excepción en caso de error de tipo std::bad_cast (o una clase derivada de std::bad_cast ).

struct Base { virtual ~Base(); }; // Base is polymorphic
struct Derived : Base {};
Base* b1 = new Derived;
Derived* d1 = dynamic_cast<Derived*>(b1); // OK; d1 points to Derived object
Base* b2 = new Base;
Derived* d2 = dynamic_cast<Derived*>(b2); // d2 is a null pointer

Arrojando constness

Un puntero a un objeto const se puede convertir en un puntero a un objeto no constante utilizando la palabra clave const_cast . Aquí usamos const_cast para llamar a una función que no es const-correcta. Solo acepta un argumento no const char* aunque nunca escribe a través del puntero:

void bad_strlen(char*);
const char* s = "hello, world!";
bad_strlen(s);                    // compile error
bad_strlen(const_cast<char*>(s)); // OK, but it's better to make bad_strlen accept const char*

const_cast puede utilizar const_cast al tipo de referencia para convertir un lvalue cualificado por const en un valor no calificado por const.

const_cast es peligroso porque hace imposible que el sistema de tipo C ++ impida que intente modificar un objeto const. Si lo hace, se traduce en un comportamiento indefinido.

const int x = 123;
int& mutable_x = const_cast<int&>(x);
mutable_x = 456; // may compile, but produces *undefined behavior*

Tipo de conversión de punning

Un puntero (referencia de referencia) a un tipo de objeto se puede convertir en un puntero (referencia de referencia) a cualquier otro tipo de objeto utilizando reinterpret_cast . Esto no llama a ningún constructor o funciones de conversión.

int x = 42;
char* p = static_cast<char*>(&x);      // error: static_cast cannot perform this conversion
char* p = reinterpret_cast<char*>(&x); // OK
*p = 'z';                              // maybe this modifies x (see below)
C ++ 11

El resultado de reinterpret_cast representa la misma dirección que el operando, siempre que la dirección esté alineada adecuadamente para el tipo de destino. De lo contrario, el resultado no se especifica.

int x = 42;
char& r = reinterpret_cast<char&>(x);
const void* px = &x;
const void* pr = &r;
assert(px == pr); // should never fire
C ++ 11

El resultado de reinterpret_cast no está especificado, excepto que un puntero (referencia respectiva) sobrevivirá un viaje de ida y vuelta desde el tipo de origen al tipo de destino y viceversa, siempre que el requisito de alineación del tipo de destino no sea más estricto que el del tipo de origen.

int x = 123;
unsigned int& r1 = reinterpret_cast<unsigned int&>(x);
int& r2 = reinterpret_cast<int&>(r1);
r2 = 456; // sets x to 456

En la mayoría de las implementaciones, reinterpret_cast no cambia la dirección, pero este requisito no se estandarizó hasta C ++ 11.

reinterpret_cast también se puede utilizar para convertir de un tipo de puntero a datos a otro, o un tipo de función de puntero a miembro a otro.

El uso de reinterpret_cast se considera peligroso porque la lectura o escritura a través de un puntero o una referencia obtenida mediante reinterpret_cast puede desencadenar un comportamiento indefinido cuando los tipos de origen y destino no están relacionados.

Conversión entre puntero y entero

Un puntero de objeto (incluido void* ) o un puntero de función se puede convertir a un tipo entero usando reinterpret_cast . Esto solo se compilará si el tipo de destino es lo suficientemente largo. El resultado está definido por la implementación y generalmente proporciona la dirección numérica del byte en memoria a la que apuntan los punteros.

Por lo general, long o unsigned long es lo suficientemente largo para contener cualquier valor de puntero, pero esto no está garantizado por la norma.

C ++ 11

Si los tipos std::intptr_t y std::uintptr_t existen, se garantiza que son lo suficientemente largos para contener un void* (y, por lo tanto, cualquier puntero al tipo de objeto). Sin embargo, no se garantiza que sean lo suficientemente largos para contener un puntero de función.

De manera similar, reinterpret_cast se puede usar para convertir un tipo entero en un tipo de puntero. Nuevamente, el resultado está definido por la implementación, pero se garantiza que un valor de puntero no se modificará en un viaje de ida y vuelta a través de un tipo entero. El estándar no garantiza que el valor cero se convierta en un puntero nulo.

void register_callback(void (*fp)(void*), void* arg); // probably a C API
void my_callback(void* x) {
    std::cout << "the value is: " << reinterpret_cast<long>(x); // will probably compile
}
long x;
std::cin >> x;
register_callback(my_callback,
                  reinterpret_cast<void*>(x)); // hopefully this doesn't lose information...

Conversión por constructor explícito o función de conversión explícita

Una conversión que implique llamar a un constructor explícito o una función de conversión no se puede hacer de manera implícita. Podemos solicitar que la conversión se realice explícitamente usando static_cast . El significado es el mismo que el de una inicialización directa, excepto que el resultado es temporal.

class C {
    std::unique_ptr<int> p;
  public:
    explicit C(int* p) : p(p) {}
};
void f(C c);
void g(int* p) {
    f(p);                 // error: C::C(int*) is explicit
    f(static_cast<C>(p)); // ok
    f(C(p));              // equivalent to previous line
    C c(p); f(c);         // error: C is not copyable
}

Conversión implícita

static_cast puede realizar cualquier conversión implícita. Este uso de static_cast ocasionalmente puede ser útil, como en los siguientes ejemplos:

  • Cuando se pasan argumentos a una elipsis, el tipo de argumento "esperado" no se conoce estáticamente, por lo que no se producirá una conversión implícita.

    const double x = 3.14;
    printf("%d\n", static_cast<int>(x)); // prints 3
    // printf("%d\n", x); // undefined behaviour; printf is expecting an int here
    // alternative:
    // const int y = x; printf("%d\n", y);
    

    Sin la conversión explícita de tipos, se pasaría un objeto double a los puntos suspensivos y se produciría un comportamiento indefinido.

  • Un operador de asignación de clase derivada puede llamar a un operador de asignación de clase base de la siguiente manera:

    struct Base { /* ... */ };
    struct Derived : Base {
        Derived& operator=(const Derived& other) {
            static_cast<Base&>(*this) = other;
            // alternative:
            // Base& this_base_ref = *this; this_base_ref = other;
        }
    };
    

Enum las conversiones

static_cast puede convertir de un tipo de punto flotante o entero a un tipo de enumeración (ya sea con o sin ámbito), y viceversa. También puede convertir entre tipos de enumeración.

  • La conversión de un tipo de enumeración sin ámbito a un tipo aritmético es una conversión implícita; es posible, pero no necesario, usar static_cast .
C ++ 11
  • Cuando un tipo de enumeración de ámbito se convierte en un tipo aritmético:

    • Si el valor de la enumeración se puede representar exactamente en el tipo de destino, el resultado es ese valor.
    • De lo contrario, si el tipo de destino es un tipo entero, el resultado no se especifica.
    • De lo contrario, si el tipo de destino es un tipo de punto flotante, el resultado es el mismo que el de la conversión al tipo subyacente y luego al tipo de punto flotante.

    Ejemplo:

    enum class Format {
        TEXT = 0,
        PDF = 1000,
        OTHER = 2000,
    };
    Format f = Format::PDF;
    int a = f;                         // error
    int b = static_cast<int>(f);       // ok; b is 1000
    char c = static_cast<char>(f);     // unspecified, if 1000 doesn't fit into char
    double d = static_cast<double>(f); // d is 1000.0... probably
    
  • Cuando un entero o tipo de enumeración se convierte en un tipo de enumeración:

    • Si el valor original está dentro del rango de la enumeración de destino, el resultado es ese valor. Tenga en cuenta que este valor puede ser desigual para todos los enumeradores.
    • De lo contrario, el resultado es no especificado (<= C ++ 14) o indefinido (> = C ++ 17).

    Ejemplo:

    enum Scale {
        SINGLE = 1,
        DOUBLE = 2,
        QUAD = 4
    };
    Scale s1 = 1;                     // error
    Scale s2 = static_cast<Scale>(2); // s2 is DOUBLE
    Scale s3 = static_cast<Scale>(3); // s3 has value 3, and is not equal to any enumerator
    Scale s9 = static_cast<Scale>(9); // unspecified value in C++14; UB in C++17
    
C ++ 11
  • Cuando un tipo de punto flotante se convierte en un tipo de enumeración, el resultado es el mismo que la conversión al tipo subyacente de la enumeración y luego al tipo de enumeración.

    enum Direction {
        UP = 0,
        LEFT = 1,
        DOWN = 2,
        RIGHT = 3,
    };
    Direction d = static_cast<Direction>(3.14); // d is RIGHT
    

Derivado a conversión base para punteros a miembros

Un puntero a miembro de clase derivada se puede convertir en un puntero a miembro de clase base usando static_cast . Los tipos apuntados deben coincidir.

Si el operando es un puntero nulo al valor miembro, el resultado también es un puntero nulo al valor miembro.

De lo contrario, la conversión solo es válida si el miembro al que apunta el operando existe realmente en la clase de destino, o si la clase de destino es una clase base o derivada de la clase que contiene el miembro al que apunta el operando. static_cast no comprueba la validez. Si la conversión no es válida, el comportamiento no está definido.

struct A {};
struct B { int x; };
struct C : A, B { int y; double z; };
int B::*p1 = &B::x;
int C::*p2 = p1;                              // ok; implicit conversion
int B::*p3 = p2;                              // error
int B::*p4 = static_cast<int B::*>(p2);       // ok; p4 is equal to p1
int A::*p5 = static_cast<int A::*>(p2);       // undefined; p2 points to x, which is a member
                                              // of the unrelated class B
double C::*p6 = &C::z;
double A::*p7 = static_cast<double A::*>(p6); // ok, even though A doesn't contain z
int A::*p8 = static_cast<int A::*>(p6);       // error: types don't match

nulo * a T *

En C ++, void* no se puede convertir implícitamente a T* donde T es un tipo de objeto. En su lugar, static_cast debe usarse para realizar la conversión explícitamente. Si el operando realmente apunta a un objeto T , el resultado apunta a ese objeto. De lo contrario, el resultado no se especifica.

C ++ 11

Incluso si el operando no apunta a un objeto T , siempre que el operando apunte a un byte cuya dirección esté correctamente alineada para el tipo T , el resultado de la conversión apunta al mismo byte.

// allocating an array of 100 ints, the hard way
int* a = malloc(100*sizeof(*a));                    // error; malloc returns void*
int* a = static_cast<int*>(malloc(100*sizeof(*a))); // ok
// int* a = new int[100];                           // no cast needed
// std::vector<int> a(100);                         // better

const char c = '!';
const void* p1 = &c;
const char* p2 = p1;                           // error
const char* p3 = static_cast<const char*>(p1); // ok; p3 points to c
const int* p4 = static_cast<const int*>(p1);   // unspecified in C++03;
                                               // possibly unspecified in C++11 if
                                               // alignof(int) > alignof(char)
char* p5 = static_cast<char*>(p1);             // error: casting away constness

Casting estilo c

El casting de estilo C se puede considerar el casting de "Mejor esfuerzo" y se denomina así porque es el único casting que se podría usar en C. La sintaxis de este casting es la (NewType)variable .

Cuando se utiliza este lanzamiento, usa uno de los siguientes lanzamientos de c ++ (en orden):

  • const_cast<NewType>(variable)
  • static_cast<NewType>(variable)
  • const_cast<NewType>(static_cast<const NewType>(variable))
  • reinterpret_cast<const NewType>(variable)
  • const_cast<NewType>(reinterpret_cast<const NewType>(variable))

La conversión funcional es muy similar, aunque como pocas restricciones como resultado de su sintaxis: NewType(expression) . Como resultado, solo se pueden convertir los tipos sin espacios.

Es mejor usar el nuevo c ++ cast, porque es más legible y se puede detectar fácilmente en cualquier lugar dentro de un código fuente de C ++ y los errores se detectarán en tiempo de compilación, en lugar de en tiempo de ejecución.

Como este lanzamiento puede dar lugar a reinterpret_cast no deseado, a menudo se considera peligroso.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow