C++

Conversions standard C++

Conversions standard C++
Il existe deux types d'entités en C++, les types fondamentaux et les types composés. Les types fondamentaux sont les types scalaires. Les types composés sont les autres types d'entités. La conversion peut avoir lieu d'un type d'entité à un autre type approprié. Considérez le programme suivant :

#inclure
#inclure
en utilisant l'espace de noms std ;
int main()

int rt1 = sqrt(5);
int rt2 = sqrt(8) ;
cout<renvoie 0 ;

La sortie est 2, 2, ce qui signifie que le programme a renvoyé la racine carrée de 5 comme 2 et la racine carrée de 8 également comme 2. Ainsi, les deux premières déclarations de la principale() fonction ont terrassé les réponses de la racine carrée de 5 et la racine carrée de 8. Cet article ne traite pas du revêtement de sol ou du plafond en C++. Au lieu de cela, cet article traite de la conversion d'un type C++ en un autre type C++ approprié ; indiquant toute approximation de valeur effectuée, perte de précision ou contrainte ajoutée ou supprimée. Des connaissances de base en C++ sont un prérequis pour comprendre cet article.

Contenu de l'article

  • Conversions intégrales
  • Conversions à virgule flottante
  • Conversions flottantes-intégrales
  • Classement des conversions d'entiers
  • Promotions intégrales
  • Conversions arithmétiques habituelles
  • Promotion à virgule flottante
  • Conversions de pointeur
  • Conversions de fonction en pointeur
  • Conversions booléennes
  • Lvalue, prvalue et xvalue
  • Valeur X
  • Conversions Lvalue à rvalue
  • Conversions tableau en pointeur
  • Conversions de fonction en pointeur
  • Conversions de matérialisation temporaires
  • Conversions de qualifications
  • Conclusion

Conversions intégrales

Les conversions intégrales sont des conversions entières. Les entiers non signés incluent "unsigned char", "unsigned short int", "unsigned int", "unsigned long int" et "unsigned long long int." Les entiers signés correspondants incluent "signed char", "short int", "int", "long int" et "long long int.” Chaque type int doit être contenu dans autant d'octets que son prédécesseur. Pour la plupart des systèmes, un type d'entité peut être converti en un type correspondant sans aucun problème. Le problème se produit lors de la conversion d'un type de plage plus large en un type de plage plus petit, ou lors de la conversion d'un nombre signé en un nombre non signé correspondant.

Chaque compilateur a une valeur maximale qu'il peut prendre pour le court int. Si un nombre supérieur à ce maximum, destiné à un int, est attribué à l'int court, le compilateur suivra un algorithme et renverra un nombre dans la plage de l'int court. Si le programmeur a de la chance, le compilateur avertira des problèmes liés à l'utilisation d'une conversion inappropriée. La même explication s'applique aux conversions d'autres types int.

L'utilisateur doit consulter la documentation du compilateur pour déterminer les valeurs limites pour chaque type d'entité.

Si un nombre int court signé négatif doit être converti en un nombre int court non signé, le compilateur suivra un algorithme et renverra un nombre positif dans la plage de l'int court non signé. Ce genre de conversion doit être évité. La même explication s'applique aux conversions d'autres types int.

Tout nombre entier, sauf 0, peut être converti en vrai booléen. 0 est converti en booléen faux. Le code suivant illustre cela :

int a = -27647;
flotteur b = 2.5 ;
entier c = 0;
bool a1 = a;
bool b1 = b;
bool c1 = c;
cout<cout<cout<La sortie est :

1 pour vrai
1 pour vrai
0 pour faux

Conversions à virgule flottante

Les types à virgule flottante incluent « flotteur », « double » et « double long.” Les types à virgule flottante ne sont pas regroupés en signés et non signés, comme les entiers. Chaque type peut avoir un numéro signé ou non signé. Un type à virgule flottante doit avoir au moins la même précision que son prédécesseur. C'est-à-dire que « long double » devrait avoir une précision égale ou supérieure à « double » et « double » devrait avoir une précision égale ou supérieure à « float."

N'oubliez pas que la plage d'un type à virgule flottante n'est pas continue ; c'est plutôt par petites étapes. Plus la précision du type est grande, plus les pas sont petits et plus le nombre d'octets pour stocker le nombre est grand. Ainsi, lorsqu'un nombre à virgule flottante est converti d'un type de précision inférieure à un type de précision supérieure, le programmeur doit accepter une fausse augmentation de la précision et une augmentation possible du nombre d'octets pour le stockage de nombres. Lorsqu'un nombre à virgule flottante est converti d'un type de précision supérieure à un type de précision inférieure, le programmeur doit accepter une perte de précision. Si le nombre d'octets pour le stockage des nombres doit être réduit, le compilateur suivra un algorithme et renverra un nombre en remplacement (ce qui n'est probablement pas ce que le programmeur veut). Aussi, gardez à l'esprit les problèmes hors de portée.

Conversions flottantes-intégrales

Un nombre à virgule flottante est converti en un entier en tronquant la partie fractionnaire. Le code suivant illustre cela :

flotteur f = 56.953 ;
entier i = f;
cout<La sortie est 56. Les plages pour le flottant et l'entier doivent être compatibles.

Lorsqu'un entier est converti en flottant, la valeur affichée comme flottant est la même que celle saisie comme entier. Cependant, l'équivalent flottant peut être la valeur exacte ou avoir une légère différence fractionnaire qui n'est pas affichée. La raison de la différence fractionnaire est que les nombres à virgule flottante sont représentés dans l'ordinateur par petits pas fractionnaires, et donc représenter exactement l'entier serait une coïncidence. Ainsi, bien que l'entier affiché sous forme de flottant soit le même que celui qui a été tapé, l'affichage peut être une approximation de ce qui est stocké.

Classement des conversions d'entiers

Tout type entier a un rang qui lui a été attribué. Ce classement aide à la conversion. Le classement est relatif ; les rangs ne sont pas à des niveaux fixes. À l'exception de char et de char signé, deux entiers signés n'ont pas le même rang (en supposant que char est signé). Les types entiers non signés ont le même classement que leurs types entiers signés correspondants. Le classement est le suivant :

  • En supposant que char est signé, alors char et char signé ont le même rang.
  • Le rang d'un type entier signé est supérieur au rang d'un type entier signé d'un plus petit nombre d'octets de stockage. Ainsi, le rang de l'int long long signé est supérieur au rang de l'int long signé, qui est supérieur au rang de l'int signé, qui est supérieur au rang de l'int court signé, qui est supérieur au rang de char signé.
  • Le rang de tout type d'entier non signé est égal au rang du type d'entier signé correspondant.
  • Le rang des caractères non signés est égal au rang des caractères signés.
  • bool a le rang le plus bas ; son rang est inférieur à celui du caractère signé.
  • char16_t a le même rang que le short int. char32_t a le même rang que l'int. Pour le compilateur g++, wchar_t a le même rang que l'int.

Promotions intégrales

Les promotions intégrales sont des promotions entières. Il n'y a aucune raison pour qu'un entier de moins d'octets ne puisse pas être représenté par un entier de plus d'octets. Integer Promotions s'occupe de tout ce qui suit :

  • Un int court signé (deux octets) peut être converti en un int signé (quatre octets). Un int court non signé (deux octets) peut être converti en un int non signé (quatre octets). Remarque : convertir un int court en un int long ou un int long long entraîne un gaspillage d'octets de stockage (emplacement de l'objet) et un gaspillage de mémoire. Bool, char16_t, char32_t et wchar_t sont exemptés de cette promotion (avec le compilateur g++, char32_t et wchar_t ont le même nombre d'octets).
  • Avec le compilateur g++, un type char16_t peut être converti en un type int signé ou un type int non signé ; un type char32_t peut être converti en un type int signé ou un type int non signé ; et un type wchar_t peut être converti en un type int signé ou non signé.
  • Un type bool peut être converti en un type int. Dans ce cas, vrai devient 1 (quatre octets) et faux devient 0 (quatre octets). Int peut être signé ou signé.
  • La promotion d'entiers existe également pour le type d'énumération sans étendue - voir plus loin.

Conversions arithmétiques habituelles

Considérez le code suivant :

flotteur f = 2.5 ;
entier i = f;
cout<Le code compile sans indiquer d'avertissement ou d'erreur, donnant la sortie de 2, ce qui n'est probablement pas ce qui était attendu. = est un opérateur binaire car il prend un opérande gauche et droit. Considérez le code suivant :

entier i1 = 7 ;
entier i2 = 2;
flotteur flt = i1 / i2;
cout<La sortie est 3, mais c'est faux ; il était censé être 3.5. L'opérateur de division, /, est aussi un opérateur binaire.

C++ a des conversions arithmétiques habituelles que le programmeur doit connaître pour éviter les erreurs de codage. Les conversions arithmétiques habituelles sur les opérateurs binaires sont les suivantes :

  • Si l'un des opérandes est du type « long double », alors l'autre sera converti en long double.
  • Sinon, si l'un des opérandes est double, l'autre sera converti en double.
  • Sinon, si l'un des opérandes est flottant, l'autre sera converti en flottant. Dans le code ci-dessus, le résultat de i1/i2 est officiellement 2 ; c'est pourquoi flt vaut 2. Le résultat du binaire, /, est appliqué comme opérande droit à l'opérateur binaire, =. Ainsi, la valeur finale de 2 est un flottant (pas un int).

AUTREMENT, LA PROMOTION ENTIER AURA LIEU COMME SUIT :

  • Si les deux opérandes sont du même type, aucune autre conversion n'a lieu.
  • Sinon, si les deux opérandes sont des types entiers signés ou les deux sont des types entiers non signés, alors l'opérande du type avec le rang entier le plus bas sera converti en type de l'opérande avec le rang le plus élevé.
  • Sinon, si un opérande est signé et l'autre non signé, et si le type d'opérande non signé est supérieur ou égal au rang du type d'opérande signé, et si la valeur de l'opérande signé est supérieure ou égale à zéro, alors l'opérande signé sera converti dans le type d'opérande non signé (avec plage prise en considération). Si l'opérande signé est négatif, le compilateur suivra un algorithme et renverra un nombre qui peut ne pas être acceptable pour le programmeur.
  • Sinon, si un opérande est un type entier signé et l'autre est un type entier non signé, et si toutes les valeurs possibles du type de l'opérande avec le type entier non signé peuvent être représentées par le type entier signé, alors le type entier non signé sera être converti au type de l'opérande du type entier signé.
  • Sinon, les deux opérandes (un char et un bool, par exemple) seraient convertis au type entier non signé.

Promotion à virgule flottante

Les types à virgule flottante incluent « flotteur », « double » et « double long.” Un type à virgule flottante doit avoir au moins la même précision que son prédécesseur. La promotion à virgule flottante permet la conversion de flottant en double ou de double en double long.

Conversions de pointeur

Un pointeur d'un type d'objet ne peut pas être affecté à un pointeur d'un autre type d'objet. Le code suivant ne compilera pas :

int id = 6;
int* intPtr = &id;
flottant idf = 2.5 ;
float* floatPtr = &idf;
intPtr = floatPtr; // erreur ici

Un pointeur nul est un pointeur dont la valeur d'adresse est zéro. Un pointeur nul d'un type d'objet ne peut pas être affecté à un pointeur nul d'un autre type d'objet. Le code suivant ne compilera pas :

int id = 6;
int* intPtr = &id;
intPtr = 0 ;
flottant idf = 2.5 ;
float* floatPtr = &idf;
floatPtr = 0;
intPtr = floatPtr; // erreur ici

Un pointeur nul const d'un type d'objet ne peut pas être affecté à un pointeur nul const d'un type d'objet différent. Le code suivant ne compilera pas :

int id = 6;
int* intPtr = &id;
int* const intPC = 0;
flottant idf = 2.5 ;
float* floatPtr = &idf;
float* const floatPC = 0;
intPC = floatPC; // erreur ici

Un pointeur nul peut recevoir une valeur d'adresse différente pour son type. Le code suivant illustre cela :

flottant idf = 2.5 ;
float* floatPtr = 0;
floatPtr = &idf;
cout<<*floatPtr<<'\n';

La sortie est 2.5.

Comme prévu, une constante de pointeur nul ne peut se voir attribuer aucune valeur d'adresse de son type. Le code suivant ne compilera pas :

flottant idf = 2.5 ;
float* const floatPC = 0;
floatPC = &idf; //erreur ici

Cependant, une constante de pointeur nul peut être affectée à un pointeur ordinaire, mais du même type (cela est normal). Le code suivant illustre cela :

flottant idf = 2.5 ;
float* const floatPC = 0;
float* floatPter = &idf;
floatPter = floatPC; //D'ACCORD
cout << floatPter << '\n';

La sortie est 0.

Deux valeurs de pointeur nul du même type se comparent (==) égales.

Un pointeur vers un type d'objet peut être affecté à un pointeur vers void. Le code suivant illustre cela :

flottant idf = 2.5 ;
float* floatPtr = &idf;
vide* vd;
vd = floatPtr;

Le code se compile sans avertissement ni message d'erreur.

Conversions de fonction en pointeur

Un pointeur vers une fonction qui ne lèverait pas d'exception peut être affecté à un pointeur vers la fonction. Le code suivant illustre cela :

#inclure
en utilisant l'espace de noms std ;
void fn1() nonsauf

cout << "with noexcept" << '\n';

vide fn2()

//déclarations

void (*func1)() noexcept;
void (*func2)();
int main()

fonction1 = &fn1;
fonction2 = &fn2;
fonction2 = &fn1;
func2();
renvoie 0 ;

La sortie est sans exception.

Conversions booléennes

En C++, les entités qui peuvent donner la valeur false incluent « zéro », « pointeur nul » et « pointeur de membre nul ».” Toutes les autres entités résultent en vrai. Le code suivant illustre cela :

bool a = 0.0 ; cout << a <<'\n';
float* floatPtr = 0;
bool b = floatPtr; cout << b <<'\n';
bool c = -2.5 ; cout << c <<'\n';
bool d = +2.5 ; cout << d <<'\n';

La sortie est :

0 //pour faux
0 //pour faux
1 //pour vrai
1 // pour vrai

Lvalue, prvalue et xvalue

Considérez le code suivant :

ID entier = 35 ;
int& id1 = id;
cout << id1 << '\n';

La sortie est 35. Dans le code, id et id1 sont des lvalues ​​car ils identifient un emplacement (objet) en mémoire. La sortie 35 est une valeur pr. Tout littéral, à l'exception d'un littéral de chaîne, est une valeur pr. D'autres prvalues ​​ne sont pas si évidentes, comme dans les exemples qui suivent. Considérez le code suivant :

ID entier = 62 ;
int* ptr = &id;
int* pter;

Ptr est une lvalue car elle identifie un emplacement (objet) en mémoire. Par contre, pter n'est pas une lvalue. Pter est un pointeur, mais il n'identifie aucun emplacement en mémoire (il ne pointe vers aucun objet). Donc, pter est une prvalue.

Considérez le code suivant :

vide fn()

//déclarations

vide (*func)() = &fn;
float (*functn)();

Fn() et (*func)() sont des expressions lvalue car elles identifient une entité (fonction) en mémoire. D'autre part, (*fuctn)() n'est pas une expression lvalue. (*functn)() est un pointeur vers une fonction, mais il n'identifie aucune entité en mémoire (il ne pointe vers aucune fonction en mémoire). Donc, (*fuctn)() est une expression prvalue.

Maintenant, considérons le code suivant :

structure S

entier n;
 ;
S obj;

S est une classe et obj est un objet instancié à partir de la classe. Obj identifie un objet en mémoire. Une classe est une unité généralisée. Donc, S n'identifie pas vraiment d'objet en mémoire. S est dit être un objet sans nom. S est aussi une expression prvalue.

L'objectif de cet article est sur prvalues. Prvalue signifie rvalue pure.

Valeur X

Xvalue signifie Expiring Value. Les valeurs temporaires sont des valeurs expirantes. Une lvalue peut devenir une xvalue. Une prvalue peut aussi devenir une xvalue. L'objectif de cet article est sur prvalues. Une xvalue est une lvalue ou une référence rvalue sans nom dont le stockage peut être réutilisé (généralement parce qu'il est proche de la fin de sa durée de vie). Considérez le code suivant qui fonctionne :

structure S

entier n;
 ;
entier q = S().n;

L'expression « int q = S().n ; » copie n'importe quelle valeur n dans q. S() n'est qu'un moyen ; ce n'est pas une expression régulièrement utilisée. S() est une prvalue dont l'utilisation l'a convertie en une valeur x.

Conversions Lvalue à rvalue

Considérez l'énoncé suivant :

entier ii = 70 ;

70 est une prvalue (rvalue) et ii est une lvalue. Maintenant, considérons le code suivant :

entier ii = 70 ;
entier tt = ii;

Dans le deuxième énoncé, ii est dans la situation d'une prvalue, donc ii y devient une prvalue. En d'autres termes, le compilateur convertit implicitement ii en une valeur pr. C'est-à-dire que lorsqu'une lvalue est utilisée dans une situation dans laquelle l'implémentation attend une prvalue, l'implémentation convertit la lvalue en une prvalue.

Conversions tableau en pointeur

Considérez le code suivant qui fonctionne :

caractère* p;
char q[] = 'a', 'b', 'c' ;
p = &q[0];
++p;
cout<<*p<<'\n';

La sortie est b. La première instruction est une expression et est un pointeur vers un caractère. Mais vers quel caractère la déclaration pointe-t-elle? - Aucun caractère. C'est donc une prvalue et non une lvalue. La deuxième instruction est un tableau dans lequel q[] est une expression lvalue. La troisième instruction transforme la prvalue, p, en une expression lvalue, qui pointe vers le premier élément du tableau.

Conversions de fonction en pointeur

Considérez le programme suivant :

#inclure
en utilisant l'espace de noms std ;
void (*func)();
vide fn()

//déclarations

int main()

fonction = &fn;
renvoie 0 ;

L'expression « void (*func)(); » est un pointeur sur une fonction. Mais vers quelle fonction l'expression pointe-t-elle? - Pas de fonction. C'est donc une prvalue et non une lvalue. Fn() est une définition de fonction, où fn est une expression lvalue. Dans main(), « func = &fn;” transforme la prvalue, func, en une expression lvalue qui pointe vers la fonction, fn().

Conversions de matérialisation temporaires

En C++, une prvalue peut être convertie en une xvalue du même type. Le code suivant illustre cela :

structure S

entier n;
 ;
entier q = S().n;

Ici, la prvalue, S(), a été convertie en xvalue. En tant que valeur x, cela ne durerait pas longtemps - voir plus d'explications ci-dessus.

Conversions de qualifications

Un type qualifié par cv est un type qualifié par le mot réservé « const » et/ou le mot réservé « volatile »."

La qualification Cv est également classée. Aucune qualification cv n'est inférieure à la qualification « const », qui est inférieure à la qualification « const volatile ». Aucune qualification cv n'est inférieure à la qualification « volatile », qui est inférieure à la qualification « const volatile ». Il existe donc deux flux de classement de qualification. Un type peut être plus qualifié de CV qu'un autre.

Un type pré-qualifié cv plus faible peut être converti en un type prvalue plus qualifié cv. Les deux types doivent être pointeur vers cv.

Conclusion

Les entités C++ peuvent être converties d'un type à un type associé implicitement ou explicitement. Cependant, le programmeur doit comprendre ce qui peut être converti et ce qui ne peut pas être converti, et sous quelle forme. La conversion peut avoir lieu dans les domaines suivants : conversions intégrales, conversions en virgule flottante, conversions en entier flottant, conversions arithmétiques habituelles, conversions de pointeur, conversions de fonction en pointeur, conversions booléennes, conversions Lvalue en rvalue, conversions tableau en pointeur , conversions de fonction en pointeur, conversions de matérialisation temporaire et conversions de qualification.

Meilleurs jeux de ligne de commande pour Linux
La ligne de commande n'est pas seulement votre plus grand allié lorsque vous utilisez Linux, elle peut également être une source de divertissement car...
Meilleures applications de mappage de manette de jeu pour Linux
Si vous aimez jouer à des jeux sur Linux avec une manette de jeu au lieu d'un système de saisie clavier et souris typique, il existe des applications ...
Outils utiles pour les joueurs Linux
Si vous aimez jouer à des jeux sur Linux, il est probable que vous ayez utilisé des applications et des utilitaires comme Wine, Lutris et OBS Studio p...