Dans le langage C, et compatible en C++, la conversion explicite d'un objet en un autre type, si elle est possible, fait précéder l'identificateur de l'objet du type dans lequel il doit être converti, le type étant parenthésé :
(type_cible) objet |
En voici quelques exemples.
Exemple 1
double d1, d2, d3;
int i = 3; int j = 10; d1 = i / j;
// {1}
|
A la ligne {1}, c'est une division entière qui est effectuée, le résultat (0) étant ensuite affecté à d1.
A la ligne {2}, c'est une division réelle qui est effectuée, chaque opérante étant explicitement converti en double, le résultat (0.3) étant ensuite affecté à d2.
A la ligne {3}, c'est une division réelle qui est effectuée, le second opérante étant explicitement converti en double, le premier est implicitement converti par le compilateur en double, le résultat (0.3) étant ensuite affecté à d3.
Exemple 2
char * pMsg = "Coucou";
cout << "Message : " << pMsg << "; Adresse : " << (void *) pMsg << endl; |
Rappelons que l'opérateur << est surchargé pour tous les types de base : si le second paramètre est un pointeur de caractères, l'injecteur interprète le désir de l'utilisateur comme étant d'afficher la chaîne correspondante (au sens des chaînes en C : NTCTS). Pour tout autre type de pointeur, l'injecteur interprète le désir de l'utilisateur comme étant d'afficher l'adresse de l'objet. Pour afficher l'adresse d'une chaîne de caractères (au sens C), il faut donc convertir le type du pointeur en n'importe quel autre type de pointeur (ici (void *)). Le résultat serait équivalent s'il avait été converti en (int *).
Exemple 3
#define SIG_ERR ((void (*)(int)) -1) |
La macro SIG_ERR correspond à la valeur numérique -1, mais ne doit pas être considérée comme un objet de type int, mais comme un pointeur de fonction ayant un paramètre int et ne renvoyant pas de résultat. La valeur de la macro est, de plus, complètement parenthésée. Cette macro est couramment utilisée dans les traitements des signaux sous Unix.
Remarque :
Cette écriture est obsolète en C++ et doit donc être évitée le plus possible. Elle doit cependant être connue, car elle est fréquemment rencontrée, en particulier lors de l'utilisation de fonctions C en C++.
type_cible (objet) |
En voici quelques exemples.
Exemple 1
double d;
int i = 3; int j = 10; d = double (i) / double (j); |
Exemple 2
char * pMsg = "Coucou";
typedef void * p_void_t; cout << "Message : " << pMsg
|
Exemple 3
typedef (void (*sighandler_t)(int);
const sighandler_t SIG_ERR = sighandler_t (-1); |
En C++, la syntaxe de la conversion nécessite que le type_cible soit un type nommé et non un type anonyme (et composé), d'où l'utilisation de typedef.
Remarques :
class CX
{ public : operator type () { /* ... */ } }; // CX |
Elle permet d'utiliser un objet de "type" CX partout où un objet de "type" type est attendu.
Premier exemple :
class CRationnel
{ int m_Num; int m_Denom; public :
operator double () { return double (m_Num) / double (m_Denom); } }; // CRationnel |
qui peut être utilisé de la façon suivante :
CRationnel R (10, 3);
cout << R << endl; // Affichage de 3.333333 |
Autre exemple :
La bibliothèque standard comporte une classe assez analogue à la classe ios suivante :
class ios // classe hypothétique (en réalité
ios_base)
{ protected : int state; // bits d'état public : int eof () const; // vrai si fin de fichier visible int fail () const; // vrai si une opération a échoué, int bad () const; // vrai si une erreur s'est produite int good () const; // vrai si aucun bit d'état n'est positionné // (précédente opération d'E/S réussie) operator bool () { return good(); } // ... }; // ios |
L'opérateur bool() permet d'utiliser un flux partout où un booléen est attendu. De plus, ce booléen est vrai lorsque le flux est valide, c'est-à-dire lorsque la précédente opération effectuée dessus n'a pas échoué et qu'une nouvelle opération peut être effectuée.
L'utilisation la plus fréquente est la suivante :
for ( ; cin >> i; ) { /* ... */ } |
Comme l'injecteur, l'extracteur >> renvoie le flux sur lequel a été effectuée l'opération. Celui-ci n'est pas de type bool, comme l'exige la syntaxe de la deuxième expression de l'instruction for. Avant de diagnostiquer une erreur de compilation, le compilateur cherche parmi les conversions implicites du C++, si l'une d'elles ne peut pas être utilisée. Ce n'est pas le cas ici, aucune conversion implicite ne transforme un flux istream (donc ios) en bool. Il recherche ensuite s'il connaît une opération permettant de passer d'un istream (donc ios) à un bool. Il utilise alors l'opérateur bool().
Au contraire, un transtypage est une demande au compilateur de considérer que le type de l'objet en est en réalité un autre.
A la lumière de ces indications, l'exemple 2 ci-dessus devrait plutôt être un transtypage, et être écrit ainsi :
Exemple 2
char * pMsg = "Coucou";
cout << "Message : " << pMsg << "; Adresse : " << reinterpret_cast <void *> (pMsg) << endl; |