C++
Explicit typkonverteringar
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:
- "Typ punning" -konverteringar, som kan användas för att komma åt minnet av en typ som om det är av en annan typ.
- Konverteringar mellan heltalstyper och pekartyper , i båda riktningarna.
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
- Från en lvalue till en xvalue, som i
std::move
. Se flytta semantik .
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)
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
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.
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
.
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
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.
Ä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.