Programmation • IoT

Chap 7 – Conversions de types et transtypage

Le C++ est fortement typé : le type d’une valeur détermine sa représentation et les opérations qui peuvent lui être appliquées. Un programme doit pourtant souvent faire interagir des valeurs de types différents. La conversion permet cette adaptation, mais elle ne doit pas être considérée comme un moyen de contourner le système de types : elle constitue une décision dont il faut comprendre les conséquences.

Certaines conversions sont implicites, car le compilateur les applique automatiquement lors d’une affectation, d’un appel de fonction ou d’une expression mêlant plusieurs types. Convertir int vers double semble naturel, tandis que convertir double vers int supprime la partie fractionnaire. Une conversion acceptée par le langage n’est donc pas nécessairement exacte ni adaptée à l’intention du programmeur.

Le moment où la conversion intervient est également essentiel. Dans double resultat = 5 / 2;, la division est d’abord effectuée entre deux entiers et produit 2, ensuite converti en 2.0. Le type de la variable destination ne modifie pas rétroactivement le calcul. Pour obtenir 2.5, il faut convertir au moins un opérande avant la division.

Les pertes d’information ne concernent pas seulement les nombres réels transformés en entiers. Elles peuvent aussi apparaître entre types entiers de capacités différentes, entre valeurs signées et non signées ou lors de la conversion de grands entiers vers un type flottant de précision limitée. L’initialisation par accolades, comme int n{8.7};, aide à détecter certaines conversions réductrices en les refusant dès la compilation.

Lorsqu’une conversion doit être volontaire et visible, le programmeur utilise un transtypage explicite. Celui-ci agit sur la valeur produite par une expression sans changer le type de la variable originale. Il peut aussi être placé avant une opération pour imposer le type avec lequel celle-ci sera évaluée. Écrire explicitement le cast indique au lecteur que le changement de type est intentionnel, mais ne garantit pas l’absence de perte.

La syntaxe héritée du C, telle que (int)x, reste valide, mais elle renseigne peu sur la nature de l’opération demandée. Le C++ moderne préfère des opérateurs spécialisés, plus visibles et plus faciles à rechercher dans le code. Leur nom ne donne pas seulement le type souhaité : il précise aussi pourquoi et comment la conversion doit être effectuée.

static_cast convient aux conversions ordinaires dont la validité peut être vérifiée à la compilation. Il permet par exemple d’écrire static_cast<double>(a) / b afin de provoquer une division réelle. Le compilateur contrôle que cette catégorie de conversion est permise, mais il n’empêche pas une troncature telle que static_cast<int>(8.7).

dynamic_cast répond à une autre question : l’objet désigné par un pointeur de classe de base appartient-il réellement au type dérivé demandé ? Dans une hiérarchie polymorphe, cette vérification est effectuée à l’exécution. En cas d’échec, un cast de pointeur retourne nullptr, tandis qu’un cast de référence déclenche une exception std::bad_cast.

const_cast modifie les qualifications d’un accès, principalement const, mais ne transforme pas la nature de l’objet original. Retirer const peut être acceptable si l’objet était réellement modifiable et seulement consulté à travers un accès constant. Tenter ensuite de modifier un objet créé comme constant produit en revanche un comportement indéfini ; cet opérateur doit donc rester exceptionnel.

reinterpret_cast est réservé à certaines conversions de bas niveau, notamment entre types de pointeurs ou entre une adresse et un entier adapté. Il ne convertit pas l’objet stocké en mémoire et ne garantit pas que le résultat puisse être déréférencé sans danger. Son emploi exige de connaître précisément le type réel, l’alignement et les règles de représentation des données.

Les quatre casts ne sont donc pas quatre syntaxes concurrentes. static_cast convertit selon des règles connues, dynamic_cast vérifie un type réel dans une hiérarchie, const_cast modifie une qualification et reinterpret_cast exprime une opération de bas niveau. Choisir le bon opérateur revient à documenter la raison de la conversion tout en conservant autant que possible les garanties offertes par le système de types.