Recherche…


Introduction

Une expression peut être explicitement converti ou casté en type T en utilisant dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> ou const_cast<T> , en fonction de ce type de fonte est destiné.

C ++ prend également en charge la notation de type cast, T(expr) et la notation cast de type C, (T)expr .

Syntaxe

  • spécificateur de type simple ( )
  • simple-type-specifier ( liste-expression )
  • simple-type-specifier braced-init-list
  • typename-specifier ( )
  • typename-specifier ( liste d'expressions )
  • typename-specifier braced- init-list
  • dynamic_cast < id-type > ( expression )
  • static_cast < id-type > ( expression )
  • reinterpret_cast < id-type > ( expression )
  • const_cast < id-type > ( expression )
  • expression- type ( type-id )

Remarques

Les six notations de la distribution ont une chose en commun:

  • La conversion en un type de référence lvalue, comme dans dynamic_cast<Derived&>(base) , produit une lvalue. Par conséquent, si vous voulez faire quelque chose avec le même objet, mais le traiter comme un type différent, vous devez le convertir en un type de référence lvalue.
  • Le passage à un type de référence rvalue, comme dans static_cast<string&&>(s) , génère une valeur.
  • Le passage à un type sans référence, comme dans (int)x , donne une valeur qui peut être considérée comme une copie de la valeur en cours de projection, mais d'un type différent de l'original.

Le mot-clé reinterpret_cast est responsable de l'exécution de deux types de conversions "dangereuses":

Le mot clé static_cast peut effectuer différentes conversions:

  • Base aux conversions dérivées

  • Toute conversion pouvant être effectuée par une initialisation directe, y compris les conversions implicites et les conversions appelant un constructeur explicite ou une fonction de conversion. Voir ici et ici pour plus de détails.

  • void , ce qui élimine la valeur de l'expression.

    // on some compilers, suppresses warning about x being unused
    static_cast<void>(x);
    
  • Entre les types arithmétiques et énumération, et entre différents types d'énumération. Voir les conversions enum

  • De pointeur vers membre de classe dérivée, pointeur sur membre de classe de base. Les types indiqués doivent correspondre. Voir la conversion pour baser la conversion des pointeurs en membres

  • void* à T* .

C ++ 11

Base à la conversion dérivée

Un pointeur sur la classe de base peut être converti en un pointeur sur la classe dérivée à l'aide de static_cast . static_cast n'effectue aucune vérification au moment de l'exécution et peut entraîner un comportement indéfini lorsque le pointeur ne pointe pas vers le type souhaité.

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

De même, une référence à une classe de base peut être convertie en une référence à une classe dérivée à l'aide de 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 le type de source est polymorphe, dynamic_cast peut être utilisé pour effectuer une conversion de base en dérivée. Il effectue un contrôle d'exécution et l'échec est récupérable au lieu de générer un comportement indéfini. Dans le cas du pointeur, un pointeur nul est renvoyé en cas d'échec. Dans le cas de référence, une exception est levée en cas d'échec du type std::bad_cast (ou d'une classe dérivée 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

Jetant la constance

Un pointeur sur un objet const peut être converti en un pointeur sur un objet non-const à l'aide du mot-clé const_cast . Ici, nous utilisons const_cast pour appeler une fonction qui n'est pas constante. Il n'accepte qu'un argument non-const char* même s'il n'écrit jamais via le pointeur:

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 à référence type peut être utilisé pour convertir une lvalue qualifiée en const en une valeur non-const-qualifiée.

const_cast est dangereux car il empêche le système de type C ++ de vous empêcher de modifier un objet const. Cela entraîne un comportement indéfini.

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

Type de conversion

Un pointeur (resp. Référence) vers un type d'objet peut être converti en un pointeur (resp. Référence) vers tout autre type d'objet à l'aide de reinterpret_cast . Cela n'appelle aucun constructeur ni aucune fonction de conversion.

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

Le résultat de reinterpret_cast représente la même adresse que l'opérande, à condition que l'adresse soit correctement alignée pour le type de destination. Sinon, le résultat n'est pas spécifié.

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

Le résultat de reinterpret_cast n'est pas spécifié, sauf qu'un pointeur (resp. Référence) survivra à un aller-retour du type source au type de destination et inversement, tant que les exigences d'alignement du type de destination ne sont pas plus strictes que celles du type source.

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

Sur la plupart des implémentations, reinterpret_cast ne modifie pas l'adresse, mais cette exigence n'a pas été normalisée avant C ++ 11.

reinterpret_cast peut également être utilisé pour convertir un type de pointeur en membre de données en un autre, ou un type de pointeur vers une fonction membre vers un autre.

L'utilisation de reinterpret_cast est considérée comme dangereuse car la lecture ou l'écriture via un pointeur ou une référence obtenue à l'aide de reinterpret_cast peut déclencher un comportement indéfini lorsque les types source et destination ne sont pas liés.

Conversion entre pointeur et entier

Un pointeur d'objet (y compris void* ) ou un pointeur de fonction peut être converti en un type entier en utilisant reinterpret_cast . Cela ne compilera que si le type de destination est suffisamment long. Le résultat est défini par l'implémentation et donne généralement l'adresse numérique de l'octet en mémoire sur lequel pointe le pointeur.

En général, long ou unsigned long est suffisamment long pour contenir une valeur de pointeur, mais cela n'est pas garanti par la norme.

C ++ 11

Si les types std::intptr_t et std::uintptr_t existent, ils sont garantis suffisamment longs pour contenir un void* (et donc tout pointeur sur le type d'objet). Cependant, ils ne sont pas garantis pour contenir un pointeur de fonction.

De même, reinterpret_cast peut être utilisé pour convertir un type entier en un type de pointeur. Là encore, le résultat est défini par la mise en œuvre, mais une valeur de pointeur est garantie inchangée par un aller-retour à travers un type entier. La norme ne garantit pas que la valeur zéro est convertie en un pointeur nul.

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

Conversion par constructeur explicite ou fonction de conversion explicite

Une conversion impliquant l'appel d'un constructeur explicite ou d'une fonction de conversion ne peut pas être effectuée implicitement. Nous pouvons demander que la conversion soit faite explicitement en utilisant static_cast . La signification est la même que celle d'une initialisation directe, sauf que le résultat est temporaire.

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
}

Conversion implicite

static_cast peut effectuer toute conversion implicite. Cette utilisation de static_cast peut parfois être utile, comme dans les exemples suivants:

  • Lors du passage d'arguments à une ellipse, le type d'argument "attendu" n'est pas connu statiquement, donc aucune conversion implicite ne se produira.

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

    Sans la conversion de type explicite, un objet double serait transmis aux points de suspension et un comportement indéfini se produirait.

  • Un opérateur d'affectation de classe dérivé peut appeler un opérateur d'affectation de classe de base comme suit:

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

Conversions Enum

static_cast peut convertir un type entier ou à virgule flottante en un type d'énumération (de portée ou non), et vice versa. Il peut également convertir entre les types d'énumération.

  • La conversion d'un type d'énumération non découpé en un type arithmétique est une conversion implicite. il est possible, mais pas nécessaire d'utiliser static_cast .
C ++ 11
  • Lorsqu'un type d'énumération de portée est converti en un type arithmétique:

    • Si la valeur de l'énumération peut être représentée exactement dans le type de destination, le résultat est cette valeur.
    • Sinon, si le type de destination est un type entier, le résultat n'est pas spécifié.
    • Sinon, si le type de destination est un type à virgule flottante, le résultat est identique à celui de la conversion au type sous-jacent, puis au type à virgule flottante.

    Exemple:

    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
    
  • Lorsqu'un type d'entier ou d'énumération est converti en un type d'énumération:

    • Si la valeur d'origine est comprise dans la plage de l'énum de destination, le résultat est cette valeur. Notez que cette valeur peut être inégale pour tous les énumérateurs.
    • Sinon, le résultat est non spécifié (<= C ++ 14) ou indéfini (> = C ++ 17).

    Exemple:

    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
  • Lorsqu'un type à virgule flottante est converti en un type d'énumération, le résultat est identique à la conversion au type sous-jacent de l'énumération, puis au type enum.

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

Dérivé de la conversion de base pour les pointeurs en membres

Un pointeur sur le membre de la classe dérivée peut être converti en un pointeur sur le membre de la classe de base à l'aide de static_cast . Les types indiqués doivent correspondre.

Si l'opérande est un pointeur nul sur la valeur du membre, le résultat est également un pointeur nul sur la valeur du membre.

Sinon, la conversion n'est valide que si le membre désigné par l'opérande existe réellement dans la classe de destination ou si la classe de destination est une classe de base ou dérivée de la classe contenant le membre désigné par l'opérande. static_cast ne vérifie pas la validité. Si la conversion n'est pas valide, le comportement est indéfini.

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

annule * à T *

En C ++, void* ne peut pas être implicitement converti en T*T est un type d'objet. À la place, static_cast doit être utilisé pour effectuer la conversion de manière explicite. Si l'opérande pointe réellement sur un objet T , le résultat pointe sur cet objet. Sinon, le résultat n'est pas spécifié.

C ++ 11

Même si l'opérande ne pointe pas sur un objet T , tant que l'opérande pointe sur un octet dont l'adresse est correctement alignée pour le type T , le résultat des points de conversion sur le même octet.

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

Coulée de style C

Le casting C-Style peut être considéré comme étant le «meilleur effort» et est nommé ainsi car il est le seul qui puisse être utilisé en C. La syntaxe de cette distribution est la (NewType)variable .

Chaque fois que cette conversion est utilisée, elle utilise l'un des modèles c ++ suivants (dans l'ordre):

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

Le casting fonctionnel est très similaire, bien que quelques restrictions résultent de sa syntaxe: NewType(expression) . Par conséquent, seuls les types sans espace peuvent être convertis.

Il est préférable d'utiliser le nouveau cast c ++, car plus lisible et facilement repérable n'importe où dans un code source C ++ et que les erreurs seront détectées au moment de la compilation, plutôt qu'au moment de l'exécution.

Comme cette distribution peut entraîner un reinterpret_cast involontaire, elle est souvent considérée comme dangereuse.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow