Conversions standard

Le langage C++ définit les conversions entre ses types fondamentaux. Il définit également les conversions pour les types dérivés de pointeur, de référence et de pointeur vers membre. Ces conversions sont appelées conversions standard.

Cette section décrit les conversions standard suivantes :

  • Promotions intégrales

  • Conversions intégrales

  • Conversions flottantes

  • Conversions flottantes et intégrales

  • Conversions arithmétiques

  • Conversions de pointeurs

  • Conversions de références

  • Conversions de pointeur vers membre

    Remarque

    Les types définis par l'utilisateur peuvent spécifier leurs propres conversions. La conversion de types définis par l’utilisateur est abordée dans les constructeurs et les conversions.

Le code suivant provoque des conversions (dans cet exemple, promotions intégrales) :

long  long_num1, long_num2;
int   int_num;

// int_num promoted to type long prior to assignment.
long_num1 = int_num;

// int_num promoted to type long prior to multiplication.
long_num2 = int_num * long_num2;

Le résultat d'une conversion est une l-value uniquement si elle produit un type référence. Par exemple, une conversion définie par l’utilisateur déclarée comme operator int&() renvoie une référence et est une valeur l. Toutefois, une conversion déclarée comme operator int() retourne un objet et n’est pas une valeur l.

Promotions intégrales

Les objets d’un type intégral peuvent être convertis en un autre type intégral plus large, c’est-à-dire un type qui peut représenter un plus grand ensemble de valeurs. Ce type de conversion élargi est appelé promotion intégrale. Avec la promotion intégrale, vous pouvez utiliser les types suivants dans une expression où un autre type intégral peut être utilisé :

  • Objets, littéraux et constantes de type char et short int

  • Types d'énumération

  • int champs de bits

  • Énumérateurs

Les promotions C++ sont « préservant la valeur », car la valeur après la promotion est garantie comme la valeur avant la promotion. Dans les promotions de préservation de la valeur, les objets de types intégraux plus courts (tels que les champs de bits ou les objets de type char) sont promus en type int s’ils int peuvent représenter la plage complète du type d’origine. Si int vous ne pouvez pas représenter la plage complète de valeurs, l’objet est promu en type unsigned int. Bien que cette stratégie soit identique à celle utilisée par standard C, les conversions de préservation des valeurs ne conservent pas la « signature » de l’objet.

Les promotions de type conservation-valeur et les promotions qui conservent l'entier non signé produisent normalement les mêmes résultats. Toutefois, ils peuvent produire des résultats différents si l’objet promu apparaît comme suit :

  • Opérande de /, , %/=, %=<, <=, , >, ou>=

    Ces opérateurs se basent sur un signe pour déterminer le résultat. Les promotions de préservation des valeurs et de préservation des signes produisent des résultats différents lorsqu’elles sont appliquées à ces opérandes.

  • Opérande gauche de >> ou >>=

    Ces opérateurs traitent les quantités signées et non signées différemment dans une opération de décalage. Pour les quantités signées, une opération de décalage droit propage le bit de signe dans les positions de bits libérées, tandis que les positions de bits libérées sont remplies à zéro dans des quantités non signées.

  • Argument d’une fonction surchargée, ou opérande d’un opérateur surchargé, qui dépend de la signature du type d’opérande pour la correspondance d’arguments. Pour plus d’informations sur la définition d’opérateurs surchargés, consultez Opérateurs surchargés.

Conversions intégrales

Les conversions intégrales sont des conversions entre les types intégraux. Les types intégraux sont , (ou short int), longint, et long long. shortchar Ces types peuvent être qualifiés signed avec ou unsigned, et unsigned peuvent être utilisés comme raccourcis pour unsigned int.

Signé à unsigned

Les objets de types intégraux signés peuvent être convertis en types non signés correspondants. Lorsque ces conversions se produisent, le modèle de bits réel ne change pas. Toutefois, l’interprétation des données change. Prenons le code suivant :

#include <iostream>

using namespace std;
int main()
{
    short  i = -3;
    unsigned short u;

    cout << (u = i) << "\n";
}
// Output: 65533

Dans l’exemple précédent, un signed short, est idéfini et initialisé en nombre négatif. L’expression (u = i) est i convertie en une unsigned short avant l’affectation u.

Non signé

Les objets de types intégraux non signés peuvent être convertis en types signés correspondants. Toutefois, si la valeur non signée est en dehors de la plage représentée du type signé, le résultat n’aura pas la valeur correcte, comme illustré dans l’exemple suivant :

#include <iostream>

using namespace std;
int main()
{
short  i;
unsigned short u = 65533;

cout << (i = u) << "\n";
}
//Output: -3

Dans l’exemple précédent, u est un unsigned short objet intégral qui doit être converti en quantité signée pour évaluer l’expression (i = u). Étant donné que sa valeur ne peut pas être correctement représentée dans un signed short, les données sont mal interprétées comme indiqué.

Conversions de nombres à virgule flottante

Un objet d’un type flottant peut être converti en un type flottant plus précis, autrement dit, la conversion n’entraîne aucune perte de signification. Par exemple, les conversions de float vers double ou de double vers long double sont sécurisées et la valeur n’est pas modifiée.

Un objet d’un type flottant peut également être converti en un type moins précis, s’il se trouve dans une plage représentée par ce type. (Voir Limites flottantes pour les plages de types flottants.) Si la valeur d’origine n’est pas représentée précisément, elle peut être convertie en valeur représentant la valeur la plus élevée suivante ou la valeur représentant la plus faible suivante. Le résultat n’est pas défini s’il n’existe aucune valeur de ce type. Prenons l’exemple suivant :

cout << (float)1E300 << endl;

La valeur maximale représentée par type float est 3,402823466E38, qui est un nombre beaucoup plus petit que 1E300. Par conséquent, le nombre est converti en infini, et le résultat est « inf ».

Conversions entre types intégraux et à virgule flottante

Certaines expressions peuvent provoquer la conversion des objets de type flottant en types intégraux, ou vice versa. Lorsqu’un objet de type intégral est converti en type flottant et que la valeur d’origine n’est pas représentée exactement, le résultat est la valeur supérieure suivante ou la valeur de représentation inférieure suivante.

Lorsqu’un objet de type flottant est converti en type intégral, la partie fractionnaire est tronquée ou arrondie à zéro. Un nombre tel que 1,3 est converti en 1, et -1.3 est converti en -1. Si la valeur tronquée est supérieure à la valeur représentée la plus élevée ou inférieure à la valeur représentée la plus basse, le résultat n’est pas défini.

Conversions arithmétiques

De nombreux opérateurs binaires (abordés dans Expressions avec des opérateurs binaires) provoquent des conversions d’opérandes et produisent des résultats de la même façon. Les conversions de ces opérateurs sont appelées conversions arithmétiques habituelles. Les conversions arithmétiques d’opérandes qui ont différents types natifs sont effectuées comme indiqué dans le tableau suivant. Les types typedef se comportent selon leurs types natifs sous-jacents.

Conditions de conversion de type

Conditions remplies Conversion
L’un ou l’autre opérande est de type long double. Un autre opérande est converti en type long double.
La condition précédente n’est pas remplie et l’un des opérandes est de type double. Un autre opérande est converti en type double.
Les conditions précédentes ne sont pas remplies et l’opérande est de type float. Un autre opérande est converti en type float.
Conditions précédentes non remplies (aucun des opérandes n’est de type flottant). Les opérandes obtiennent des promotions intégrales comme suit :

- Si l’un des opérandes est de type unsigned long, l’autre opérande est converti en type unsigned long.
- Si la condition précédente n’est pas remplie, et si l’un des opérandes est de type long et l’autre de type unsigned int, les deux opérandes sont convertis en type unsigned long.
- Si les deux conditions précédentes ne sont pas remplies et si l’un des opérandes est de type long, l’autre opérande est converti en type long.
- Si les trois conditions précédentes ne sont pas remplies et si l’un des opérandes est de type unsigned int, l’autre opérande est converti en type unsigned int.
- Si aucune des conditions précédentes n’est remplie, les deux opérandes sont convertis en type int.

Le code suivant illustre les règles de conversion décrites dans le tableau :

double dVal;
float fVal;
int iVal;
unsigned long ulVal;

int main() {
   // iVal converted to unsigned long
   // result of multiplication converted to double
   dVal = iVal * ulVal;

   // ulVal converted to float
   // result of addition converted to double
   dVal = ulVal + fVal;
}

Dans l'exemple précédent, la première instruction affiche la multiplication de deux types intégraux, iVal et ulVal. La condition remplie est que ni l’opérande n’est de type flottant, et qu’un opérande est de type unsigned int. Ainsi, l’autre opérande, est iValconverti en type unsigned int. Le résultat est ensuite affecté à dVal. La condition remplie ici est qu’un opérande est de type double, donc le unsigned int résultat de la multiplication est converti en type double.

La deuxième instruction de l’exemple précédent montre l’ajout d’un type intégral et d’un float type intégral : fVal et ulVal. La ulVal variable est convertie en type float (troisième condition dans la table). Le résultat de l’ajout est converti en type double (deuxième condition dans la table) et affecté à dVal.

Conversions de pointeurs

Les pointeurs peuvent être convertis durant l'assignation, l'initialisation, la comparaison et d'autres expressions.

Pointeur vers classes

Il existe deux cas dans lesquels un pointeur vers une classe peut être converti en un pointeur vers une classe de base.

Dans le premier cas, la classe de base spécifiée est accessible et la conversion est n'est pas ambiguë. Pour plus d’informations sur les références ambiguës de classe de base, consultez Plusieurs classes de base.

L'accessibilité d'une classe de base dépend du type d'héritage utilisé dans la dérivation. Considérez l’héritage illustré dans la figure suivante :

Diagram showing an inheritance graph and base class accessibility.

Le diagramme montre la classe de base A. La classe B hérite d’A via un public protégé privé. La classe C hérite de B par le biais de B public.

Graphique d’héritage illustrant l’accessibilité de la classe de base

Le tableau suivant indique l'accessibilité de la classe de base d'après la situation illustrée.

Type de fonction Dérivation Conversion de

B*A* pour légal ?
Fonction externe (hors portée de classe) Privée Non
Protected Non
Public Oui
Fonction membre B (dans la portée B) Privées Oui
Protected Oui
Public Oui
Fonction membre C (dans la portée C) Privée Non
Protected Oui
Public Oui

Dans le second cas, un pointeur vers une classe peut être converti en pointeur vers une classe de base lorsque vous utilisez une conversion de type explicite. Pour plus d’informations sur les conversions de types explicites, consultez l’opérateur de conversion de type explicite.

Le résultat d’une telle conversion est un pointeur vers le sous-objet, la partie de l’objet qui est complètement décrite par la classe de base.

Le code suivant définit deux classes, A et B, où B est dérivée de A. (Pour plus d’informations sur l’héritage, consultez Classes dérivées.) Il définit bObjectensuite , un objet de type B, et deux pointeurs (pA et pB) qui pointent vers l’objet.

// C2039 expected
class A
{
public:
    int AComponent;
    int AMemberFunc();
};

class B : public A
{
public:
    int BComponent;
    int BMemberFunc();
};
int main()
{
   B bObject;
   A *pA = &bObject;
   B *pB = &bObject;

   pA->AMemberFunc();   // OK in class A
   pB->AMemberFunc();   // OK: inherited from class A
   pA->BMemberFunc();   // Error: not in class A
}

Le pointeur pA est de type A *, qui peut être interprété comme signifiant « pointeur vers un objet de type A». Les membres de bObject (tels que BComponent et BMemberFunc) sont uniques au type B et sont donc inaccessibles par le biais pAde . Le pointeur pA autorise l'accès uniquement aux caractéristiques (fonctions membres et données) de l'objet définies dans la classe A.

Pointeur vers fonction

Un pointeur vers une fonction peut être converti en type void *, si le type void * est suffisamment grand pour contenir ce pointeur.

Pointeur vers void

Les pointeurs vers le type void peuvent être convertis en pointeurs vers n’importe quel autre type, mais uniquement avec un cast de type explicite (contrairement en C). Un pointeur vers n’importe quel type peut être converti implicitement en pointeur en type void. Un pointeur vers un objet incomplet d’un type peut être converti en pointeur en (implicitement) et en void arrière (explicitement). Le résultat de ce type de conversion est égal à la valeur du pointeur d'origine. Un objet est considéré comme incomplet s’il est déclaré, mais il n’existe pas d’informations suffisantes pour déterminer sa taille ou sa classe de base.

Pointeur vers un objet qui n’est pas ou volatile ne peut pas const être converti implicitement en pointeur de type void *.

Pointeurs const et volatile

C++ ne fournit pas de conversion standard d’un ou volatile d’un const type vers un type qui n’est pas ou volatilen’est pas const . Cependant, toute sorte de conversion peut être spécifiée à l'aide de casts de type explicite (y compris les conversions qui ne sont pas sécurisées).

Remarque

Les pointeurs C++ vers les membres, à l’exception des pointeurs vers des membres statiques, sont différents des pointeurs normaux et n’ont pas les mêmes conversions standard. Les pointeurs vers des membres statiques sont des pointeurs normaux et ils ont les mêmes conversions que les pointeurs normaux.

Conversions de pointeurs null

Une expression constante intégrale qui prend la valeur zéro, ou une expression castée en type pointeur, est convertie en pointeur appelé pointeur Null. Ce pointeur compare toujours inégaux à un pointeur à n’importe quel objet ou fonction valide. Une exception est des pointeurs vers des objets basés, qui peuvent avoir le même décalage et pointer vers des objets différents.

En C++11, le type nullptr doit être préféré au pointeur null de style C.

Conversions d'expression de pointeur

Toute expression avec un type de tableau peut être convertie en un pointeur du même type. Le résultat de la conversion est un pointeur vers le premier élément du tableau. L'exemple suivant illustre cette conversion :

char szPath[_MAX_PATH]; // Array of type char.
char *pszPath = szPath; // Equals &szPath[0].

Une expression qui entraîne une fonction qui retourne un type particulier est convertie en un pointeur vers une fonction qui retourne ce type, sauf lorsque :

  • L’expression est utilisée comme opérande pour l’opérateur d’adresse (&).

  • L'expression est utilisée comme opérande de l'opérateur d'appel de fonction.

Conversions de références

Une référence à une classe peut être convertie en référence à une classe de base dans les cas suivants :

  • La classe de base spécifiée est accessible.

  • La conversion n'est pas ambiguë. (Pour plus d’informations sur les références ambiguës de classe de base, consultez Plusieurs classes de base.)

Le résultat de la conversion est un pointeur vers le sous-objet qui représente la classe de base.

Pointeur vers membre

Les pointeurs vers des membres de classe peuvent être convertis durant l'assignation, l'initialisation, la comparaison et d'autres expressions. Cette section décrit les conversions de pointeur vers membre suivantes :

Pointeur vers membre de classe de base

Un pointeur vers un membre d'une classe de base peut être converti en un pointeur vers un membre d'une classe dérivée de cette classe lorsque les conditions suivantes sont remplies :

  • La conversion inverse, du pointeur vers la classe dérivée vers le pointeur de classe de base, est accessible.

  • La classe dérivée n’hérite pas pratiquement de la classe de base.

Lorsque l’opérande gauche est un pointeur vers membre, l’opérande droite doit être de type pointeur vers membre ou une expression constante qui correspond à 0. Cette assignation est valide uniquement dans les cas suivants :

  • L’opérande droite est un pointeur vers un membre de la même classe que l’opérande gauche.

  • L’opérande gauche est un pointeur vers un membre d’une classe dérivée de façon publique et non ambiguë de la classe de l’opérande droite.

Pointeur null vers les conversions de membres

Une expression constante intégrale qui prend la valeur zéro est convertie en pointeur Null. Ce pointeur compare toujours inégaux à un pointeur à n’importe quel objet ou fonction valide. Une exception est des pointeurs vers des objets basés, qui peuvent avoir le même décalage et pointer vers des objets différents.

Le code suivant illustre la définition d'un pointeur désignant le membre i dans la classe A. Le pointeur, pai, est initialisé à 0, qui est le pointeur null.

class A
{
public:
int i;
};

int A::*pai = 0;

int main()
{
}

Voir aussi

Informations de référence sur le langage C++