Outils d'utilisateurs

Outils du Site


inference_de_type

Ceci est une ancienne révision du document !


Chapitre précédent Sommaire principal Chapitre suivant

L'inférence de type

Conversion implicite de types

Lorsque l'on initialise une variable avec une littérale, on fournit deux fois l'information sur le type. Par exemple, dans le code suivant :

int const i { 123 }; 

Dans ce code, le type de la variable i et la littérale 123 sont des entiers de type int.

Si le type de la variable et de la littérale n'est pas parfaitement identique, le compilateur effectue une conversion implicite, pour adapter le type de la littérale en fonction du type de la variable. Lorsque cette conversion implicite ne pose pas de problème et que la littérale peut être convertie dans le type de la variable sans perte d'information, le compilateur ne va pas signaler cette conversion implicite.

main.cpp
#include <iostream>
 
int main() {
    int const i { 123 };
    float const c { 123 };
    std::cout << i << std::endl;
    std::cout << c << std::endl;
}

affiche :

123
123

En revanche, si la littérale ne peut pas être convertie dans le type de la variable sans perte d'information, le compilateur signalera le problème. Une perte d'information peut avoir lieu par exemple dans les conversions suivantes :

  1. une littérale de type réel dans une variable de type entier ;
  2. une littérale négative dans une variable non signée ;
  3. une littérale trop grande dans une variable de type ne pouvant contenir cette valeur.
int i { 123.000 };      // 1
unsigned int ui { -1 }; // 2
char c { 123456789};    // 3

Le compilateur signalera une erreur ou un avertissement (selon les options de compilation) pour signaler la conversion (narrowing). Par exemple, dans le cas d'une conversion d'un double vers un int :

main.cpp
#include <iostream>
 
int main() {
    int const i { 123.456 };
    std::cout << i << std::endl;
}

affiche :

main.cpp:4:13: error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
    int i { 123.456 };
            ^~~~~~~
main.cpp:4:13: note: override this message by inserting an explicit cast
    int i { 123.456 };
            ^~~~~~~
            static_cast<int>( )
main.cpp:4:13: warning: implicit conversion from 'double' to 'int' changes value 
from 123.456 to 123 [-Wliteral-conversion]
    int i { 123.456 };
          ~ ^~~~~~~
1 warning and 1 error generated.

Le premier message donne le message d'erreur de conversion (“type 'double' cannot be narrowed to 'int'” signifie “le type 'double' ne peut pas être réduit dans 'int'”).

À la fin du message d'erreur, le compilateur indique l'option de compilation qui active la vérification des erreurs de conversion -Wc++11-narrowing. Vous avez déjà vu quelques options de compilation -Wall -Wextra -pedantic. Vous n'avez utilisez cette nouvelle option, mais pourtant le compilateur a signalé le problème. La raison est que les options de compilation que vous avez utilisé -Wall -Wextra -pedantic activent d'autres options de compilation, en particulier, -Wc++11-narrowing.

Le deuxième message propose de réaliser une conversion explicite au lieu d'une conversion implicite. La raison est que autant le compilateur peut supposer qu'une conversion implicite puisse être une erreur, autant une conversion explicite est forcement un choix volontaire du développeur. Vous verrez dans un prochain chapitre l'utilisation de static_cast.

Le dernier message d'avertissement, prévenant que du fait de la conversion implicite, la littérale 123.456 est changé en littérale entier 123.

Inférence de type

On peut légitimement se poser la question de fournir deux fois la même information (le type que l'on souhaite utiliser), surtout que si on n'utilise pas le même type, il y a un risque de conversion implicite et d'erreur.

L'inférence de type consiste à laisser le compilateur définir le type d'une variable, en fonction de l'expression qu'on lui donne pour initialiser une variable. Cette expression peut être une littérale, une autre variable, une expression mathématique, une fonction, etc. La syntaxe pour utiliser l'inférence de type est la suivante :

auto ma_variable = expression;

Le mot-clé auto remplace le type de la variable et sert à indiquer au compilateur d'utiliser l'inférence de type.

Il n'est pas possible d'utiliser les syntaxes de déclaration des variables avec auto et les crochets.

Avec l'initialisation par défaut auto i {};, il n'a pas d'expression donnée et donc le compilateur n'a pas de moyen de déduire le type qu'il doit utiliser.

Avec l'initialisation avec une valeur auto i { 123 };, le type déduit n'est pas un entier, mais un tableau d'entier contenant une valeur (ce type de tableau s'appelle “initializer list”). Vous verrez dans les prochains chapitres comment utiliser les tableaux.

La déduction du type est relativement simple dans le cas d'initialisation par des littérales ou une expression :

auto i = 123;     // int
auto j = 12 + 45; // int
auto k = i;       // int

auto d = 123.456; // double
auto b = true;    // bool

Cette écriture présente deux avantages par rapport à la déclaration de variables en explicitant le type. Premièrement, cela évite de devoir écrire le type. Avec des types simples, l'avantage est réduit, mais certains types complexes sont long à écrire et source potentielle d'erreur d'écriture.

Le second avantage est d'avoir un code plus souple, en laissant le compilateur déduire les types.

problème avec “hello, world!” → ne correspond pas à string, mais à une littérale chaine de caractère (const char*). Donc par exemple, pas possible d'écrire :

auto s = "hello, world!";
cout << s.size() << endl; // erreur

Possibilité d'indiquer le type :

auto s = string { 123 }; // string
auto c = complex { 123, 456 }; // complex

auto remplace que le type (int, double, etc), pas les modificateurs (const, &, &&). Par exemple pour écrire une constante :

auto const i = 123;
Chapitre précédent Sommaire principal Chapitre suivant
inference_de_type.1402342476.txt.gz · Dernière modification: 2014/06/09 21:34 par gbdivers