Outils pour utilisateurs

Outils du site


lang:cpp:cast

Les quatre types de cast

En C++, on utilise plus un cast entre parenthèse, on utilise static_cast, dynamic_cast, const_cast ou reinterpret_cast.

static_cast

static_cast peut être utilisé pour caster les types primitifs entre eux.

double d = 0.1;
// Perte explicite de précision
float f = static_cast<float>(d);

static_cast est aussi utilisé pour caster une classe parent vers un type enfant.

struct B
{
  int f() { return 2; }
};
 
struct C : public B
{
  int f() { return 3; }
};
 
int main()
{
  B b;
  C *c = static_cast<C *>(&b);
  return c->f();
}

dynamic_cast

dynamic_cast permet de vérifier que le static_cast est possible. Il est nécessaire que la classe soit polymorphique (au moins une méthode virtuelle).

struct B {
  virtual ~B() = default;
  int f() { return 2; }
};
 
struct C : public B {
  int f() { return 3; }
};
 
struct D : public B {
  int f() { return 3; }
};
 
int main() {
  C c;
  B *b = &c;
  // d != nullptr
  D *d = static_cast<D *>(b);
  // d == nullptr
  d = dynamic_cast<D *>(b);
  return d == nullptr;
}

Il est aussi possible d'utiliser dynamic_cast avec des références. En cas d'échec, une exception std::bad_cast sera généré.

#include <typeinfo>
 
class A {
 public:
  virtual ~A() = default;
};
 
class B : public A {};
 
class C {};
 
int main() {
  B b;
  A& a = dynamic_cast<A&>(b);
  try {
    C& c = dynamic_cast<C&>(b);
  } catch (const std::bad_cast&) {
    return 1;
  }
  return 0;
}

Dynamic Casts with References Archive du 14/04/2021 le 23/06/2023

const_cast

const_cast sert uniquement à enlever un const à un type.

const char *t = "coucou";
 
void f(char *tt) {
  // Interdit
  tt[0] = 1;
}
 
int main() {
  f(const_cast<char *>(t));
  return 0;
}

reinterpret_cast

reinterpret_cast est utilisé lorsqu'on souhaite caster un pointeur vers un autre sans rapport implicite. Son utilisation est souvent synonyme de mauvaises pratiques de codage.

int main()
{
  int b[5];
  short *s = reinterpret_cast<short*>(&b[0]);
}

Les problèmes

Cast vers un parent d'un héritage multiple

Depuis Parent1, caster this vers Parent2.

Il ne faut surtout pas faire (ci-dessous). Sinon, les méthodes virtuelles (au minimum) appelleront n'importe quoi. De toute façon d'une manière générale, reinterpret_cast ne s'utilise que vers la classe la plus basse dans l'héritage.

Parent2* p = reinterpret_cast<Parent2*>(this);

Il faut faire (ci-dessous). Puis un cast naturel se fera de Enfant vers Parent2.

Parent2* p = static_cast<Enfant*>(this);

reinterpret_cast sur une classe avec héritage

Un static_cast ou un dynamic_cast ne pose pas de problème dans le cas d'héritage multiple.

Par contre, un reinterpret_cast d'un void * doit toujours se faire sur la classe la plus basse (la plus enfant). Un void * ne possède aucune information du type de la classe et donc le compilateur ne sait pas comment s'en sortir. Par exemple avec les méthodes virtuelles, il ne peut pas savoir à quelle classe appartient la première méthode en tête de la vtable. Il y a les mêmes problèmes avec les attributs de la classe.

multiple inheritance: unexpected result after cast from void * to 2nd base class Archive du 04/03/2010 le 19/12/2019

Les détecters avec sanitizer

Les sanitizer peuvent détecter les erreurs de static_cast / reinterpret_cast. Il est quand même nécessaire que la classe castée soit polymorphique.

struct A
{
  virtual int f() { return 1; }
};
 
struct B
{
  virtual int f() { return 2; }
};
 
struct C : public B {};
 
int main()
{
  A a;
  B *b = reinterpret_cast<B *>(&a);
  return b->f();
}

Les classes A et B étant identiques, il est normal que le programme s'exécute correctement.

Mais avec un sanitizer clang++ -fsanitize=undefined main.cc -o a.out -flto -fvisibility=hidden :

main.cc:17:13: runtime error: member call on address 0x7ffc03ba2878 which does not point to an object of type 'B'
0x7ffc03ba2878: note: object is of type 'A'
 fc 7f 00 00  88 4d bf 29 2b 56 00 00  78 28 ba 03 fc 7f 00 00  00 09 cf 0b 25 bc 5d 17  00 00 00 00
lang/cpp/cast.txt · Dernière modification : 2023/06/23 16:48 de root