Sök…


Introduktion

Ett uttryck kan explicit omvandlas eller gjutna till typ T med användning dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> eller const_cast<T> , beroende på vilken typ av gjuten är avsedd.

C ++ stöder också T(expr) , T(expr) och C-stil cast notation, (T)expr .

Syntax

  • simple-type-specificier ( )
  • simple-type-specificier ( expression-list )
  • enkel-typ-specifikation braced-init-lista
  • typnamn-specifier ( )
  • typnamn-specifier ( expressionslista )
  • typnamn-specifier braced-init-lista
  • dynamic_cast < type-id > ( uttryck )
  • static_cast < type-id > ( uttryck )
  • reinterpret_cast < type-id > ( uttryck )
  • const_cast < type-id > ( uttryck )
  • ( typ-id ) cast-expression

Anmärkningar

Alla sex cast-notationer har en sak gemensamt:

  • Om du gjuter till en lvalue-referenstyp, som i dynamic_cast<Derived&>(base) , ger du en lvalue. Därför, när du vill göra något med samma objekt men behandla det som en annan typ, skulle du kasta till en referenstyp för lvalue.
  • Gjutning till en referenstyp för rvalue, som i static_cast<string&&>(s) , ger en rvalue.
  • Gjutning till en icke-referenstyp, som i (int)x , ger en uppskattning, som kan betraktas som en kopia av värdet som gjutas, men med en annan typ än originalet.

Den reinterpret_cast nyckelordet är ansvarig för att utföra två olika typer av "osäkra" omvandlingar:

Nyckelordet static_cast kan utföra en mängd olika omvandlingar:

  • Bas till härledda konverteringar

  • Varje konvertering som kan göras genom en direkt initialisering, inklusive implicita konverteringar och konverteringar som kallar en uttrycklig konstruktör eller konverteringsfunktion. Se här och här för mer information.

  • Att void , vilket förkastar uttrycket.

    // on some compilers, suppresses warning about x being unused
    static_cast<void>(x);
    
  • Mellan aritmetiska och uppräkningstyper, och mellan olika uppräkningstyper. Se konvertering av enum

  • Från pekare till medlem i härledd klass, till pekare till medlem i basklass. De typer som pekas på måste matcha. Se härledd till baskonvertering för pekare till medlemmar

  • void* till T* .

C ++ 11

Bas till härledd konvertering

En pekare till basklass kan konverteras till en pekare till härledd klass med static_cast . static_cast gör ingen static_cast och kan leda till odefinierat beteende när pekaren faktiskt inte pekar på önskad typ.

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

På samma sätt kan en hänvisning till basklassen konverteras till en referens till härledd klass med 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

Om källtypen är polymorf kan dynamic_cast användas för att utföra en bas till härledd konvertering. Den utför en körtidskontroll och fel kan återvinnas istället för att producera odefinierat beteende. I pekarfallet returneras en nollpekare efter fel. I referensfallet kastas ett undantag vid misslyckande av typen std::bad_cast (eller en klass härrörande från 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

Kasta bort konstness

En pekare till ett const-objekt kan konverteras till en pekare till icke-const-objekt med hjälp av const_cast sökordet . Här använder vi const_cast att kalla en funktion som inte är const-correct. Den accepterar bara ett icke-const char* -argument även om det aldrig skriver genom pekaren:

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 till referenstyp kan användas för att konvertera ett const-kvalificerat värde till ett icke-const-kvalificerat värde.

const_cast är farligt eftersom det gör det omöjligt för systemet C ++ att förhindra att du försöker ändra ett const-objekt. Om du gör det resulterar det i odefinierat beteende.

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

Skriv peka omvandling

En pekare (resp. Referens) till en objekttyp kan konverteras till en pekare (resp. Referens) till någon annan objekttyp med hjälp av reinterpret_cast . Detta kallar inga konstruktörer eller konverteringsfunktioner.

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

Resultatet av reinterpret_cast representerar samma adress som operand, förutsatt att adressen är lämpligt anpassad för destinationstypen. Annars är resultatet ospecificerat.

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

Resultatet av reinterpret_cast är ospecificerat, förutom att en pekare (resp. Referens) kommer att överleva en rundtur från källtypen till destinationstypen och tillbaka, så länge destinationstypens justeringskrav inte är strängare än källtypens.

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

På de flesta implementationer, reinterpret_cast inte ändra adressen, men detta krav inte standardiserad förrän C ++ 11.

reinterpret_cast kan också användas för att konvertera från en pekare-till-data-medlemstyp till en annan, eller en pekare-till-medlem-funktionstyp till en annan.

Användning av reinterpret_cast anses vara farligt eftersom läsning eller skrivning genom en pekare eller referens som erhållits med reinterpret_cast kan utlösa odefinierat beteende när käll- och destinationstyper inte är relaterade.

Konvertering mellan pekare och heltal

En objektpekare (inklusive void* ) eller funktionspekare kan konverteras till en heltalstyp med reinterpret_cast . Detta kommer endast att kompileras om destinationstypen är tillräckligt lång. Resultatet är implementeringsdefinerat och ger vanligtvis den numeriska adressen för byten i minnet som pekaren pekar på.

Vanligtvis är long eller unsigned long tillräckligt lång för att hålla något pekarvärde, men detta garanteras inte av standarden.

C ++ 11

Om typerna std::intptr_t och std::uintptr_t finns, garanteras de att vara tillräckligt långa för att hålla ett void* (och därmed någon pekare till objekttyp). De garanteras dock inte vara tillräckligt långa för att hålla en funktionspekare.

På liknande sätt kan reinterpret_cast användas för att konvertera en heltalstyp till en pekartyp. Återigen är resultatet implementeringsdefinerat, men ett pekarvärde garanteras att vara oförändrat med en rundtur genom en heltalstyp. Standarden garanterar inte att värdet noll konverteras till en nollpekare.

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

Konvertering med uttrycklig konstruktör eller uttrycklig konverteringsfunktion

En konvertering som innebär att man kallar en tydlig konstruktör eller konverteringsfunktion kan inte göras implicit. Vi kan begära att konverteringen görs uttryckligen med static_cast . Betydelsen är densamma som för en direkt initialisering, förutom att resultatet är ett tillfälligt.

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
}

Implicit konvertering

static_cast kan utföra varje implicit konvertering. Denna användning av static_cast kan ibland vara användbar, till exempel i följande exempel:

  • När man överför argument till en ellips är den "förväntade" argumenttypen inte statiskt känd, så ingen implicit konvertering kommer att inträffa.

    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);
    

    Utan explicit typomvandling, en double skulle föremål föras till ellips och odefinierat beteende skulle inträffa.

  • En härledd klassuppdragsoperatör kan ringa en basklassuppdragsoperatör så:

    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 omvandlingar

static_cast kan konvertera från ett heltal eller flytande punkttyp till en uppräkningstyp (vare sig scoped eller unscoped), och vice versa. Det kan också konvertera mellan uppräkningstyper.

  • Konverteringen från en icke-kodad uppräkningstyp till en aritmetisk typ är en implicit omvandling; det är möjligt, men inte nödvändigt, att använda static_cast .
C ++ 11
  • När en scoped-uppräkningstyp konverteras till en aritmetisk typ:

    • Om enums värde kan representeras exakt i destinationstypen är resultatet det värdet.
    • Annars, om destinationstypen är en heltalstyp, är resultatet ospecificerat.
    • Annars, om destinationstypen är en flytande punkttyp, är resultatet detsamma som resultatet för att konvertera till den underliggande typen och sedan till den flytande punkttypen.

    Exempel:

    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
    
  • När ett heltal eller en uppräkningstyp konverteras till en uppräkningstyp:

    • Om det ursprungliga värdet ligger inom destinationsenums intervall är resultatet det värdet. Observera att detta värde kan vara olikt för alla teller.
    • Annars är resultatet ospecificerat (<= C ++ 14) eller odefinierat (> = C ++ 17).

    Exempel:

    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
  • När en flytande punkttyp konverteras till en uppräkningstyp är resultatet detsamma som att konvertera till enums underliggande typ och sedan till enumtyp.

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

Härledd för att basera konvertering för pekare till medlemmar

En pekare till medlem i härledd klass kan konverteras till en pekare till medlem av basklassen med static_cast . De typer som pekas på måste matcha.

Om operand är ett nollpekare till medlemsvärde är resultatet också ett nollpekare till medlemsvärde.

I annat fall är konverteringen endast giltig om det medlem som pekas av operanden faktiskt existerar i destinationsklassen, eller om destinationsklassen är en bas- eller härledd klass i klassen som innehåller det medlem som pekats av operand. static_cast kontrollerar inte giltighet. Om konverteringen inte är giltig, är beteendet odefinierat.

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

ogiltig * till T *

I C ++ kan inte void* implicit konverteras till T* där T är en objekttyp. I static_cast bör static_cast användas för att utföra konverteringen uttryckligen. Om operanden faktiskt pekar på ett T objekt pekar resultatet på det objektet. Annars är resultatet ospecificerat.

C ++ 11

Även om operanden inte pekar på ett T objekt, så länge operanden pekar på en byte vars adress är korrekt anpassad för typen T , pekar resultatet av konverteringen till samma 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

Gjutning i C-stil

C-Style-gjutning kan betraktas som "Bästa ansträngning" -gjutning och benämns så att det är den enda roll som kan användas i C. Syntaxen för denna roll är (NewType)variable .

När denna roll används, använder den en av följande c ++ casts (i ordning):

  • 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))

Funktionell gjutning är mycket lik, dock som några begränsningar som resultat av syntaxen: NewType(expression) . Som ett resultat kan endast typer utan mellanrum kastas till.

Det är bättre att använda nya c ++ cast, eftersom det är mer läsbart och lätt kan upptäckas var som helst i en C ++ källkod och fel kommer att upptäckas under kompileringstid istället under körtid.

Eftersom denna roll kan resultera i oavsiktlig reinterpret_cast anses det ofta vara farligt.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow