Un des premiers mots-clefs que les développeurs utilisent lors du passage au C++11/14/17 est auto. Cet article a pour but de couvrir les différents usages d’auto, au travers d’exemples plus ou moins complexes, combinés à d’autres ajouts du C++ moderne, ainsi que les pièges à éviter.
Historique du mot-clef auto
Le mot-clef auto existe depuis le langage C. Il avait une signification bien particulière et était rarement utilisé. Ce dernier servait à qualifier une variable à l’aide d’une portée automatique (détruite automatiquement lorsque cette variable sortait de cette portée).
Le mot-clef est très peu rencontré dans du code C, car les variables sont déjà automatiquement déjà qualifiées par la portée déterminée par auto.
Output :
Ici, la variable i n'existe que dans la portée de cette fonction.
Auto, pourquoi l'utiliser ?
Auto est désormais utilisé à la place d’un type (sans en être un), servant à inférer une expression. En d’autres termes : déduire le type d’une expression. Il peut être associé à des qualificateurs comme les références ou const.
Voici une liste de ses avantages :
Force l'initialisation d'une variable, évitant au développeur un oubli d'initialisation, renforçant ainsi la sécurité du code
Evite les conversions implicites de types
Renforce l'aspect sémantique, en évitant au développeur de se soucier de l'aspect syntaxique
Généralement un gain d'efficacité quand du code est écrit, particulièrement pour les tests unitaires ou seul l'aspect fonctionnel est intéressant
Passons maintenant à plusieurs exemples de déclaration basique :
On constate que les déclarations sont identiques à une
déclaration de forme auto nom = expression. On remarque aussi que les
qualificateurs sont bien présents (const et référence).
Maintenant, on peut constater plusieurs choses, comment obtenir un entier non signé ? un float ? une string ? Passons-les en revue :
Pour les utiliser, il faut utiliser les literals du C++14. On ne peut pas obtenir directement un type float ou unsigned sans eux. Pour les string, il faut utiliser le using namespace std ::string_literals, afin d’accéder à l’opérateur "" s.
Après ces quelques exemples de déclarations, passons aux déclarations de types complexes.
Ici, grâce au C++11, on obtient des initializer_list qui sont des objets proxy légers, donnant accès à un tableau d’objets de type const T et on initialise des containers génériques de la STL à l’aide de ces objets.
Passons à des cas un peu spéciaux, l’initialisation de rvalues références avec auto.
Sortie console :
Rappel : decltype sert à inspecter le type d’une entité ou d’une expression.
Que s’est-il passé ? La présence des && ne signifie-t-elle pas toujours rvalue référence ? Et bien non, pas dans ce cas ! Pour être exact, && signifie parfois rvalue référence OU lvalue. Donc parfois, il s’agit d’une simple référence.
Comment les détecter ? Voici quelques règles :
Si le type d'une expression est une référence Ivalue, cette expression est une Ivalue
Si on peut prendre l'adresse d'une expression, il s'agit une Ivalue
Sinon, c'est une Rvalue
En parlant de decltype, depuis C++14, il est possible d'utiliser decltype(auto). Exemple :
Decltype(auto) dans une déclaration de variable utilise les règles de déduction de type de decltype, et auto est remplacé par l’expression de son initialiseur.
Et pour une fonction ?
Ici, le decltype est utilisé pour indiquer la valeur de retour de la fonction, et était nécessaire jusqu’au C++14. Il n’est maintenant plus nécessaire d’écrire cette syntaxe afin d’obtenir la valeur de retour :
Rappel : constexpr est un mot clef qui indique qu’il est possible d’évaluer une expression, une fonction, une value au moment de la compilation.
Auto sert aussi dans le cadre des lambdas, quelques exemples :
Sortie console :
Reprenons nos exemples de liste, vecteur et set déclarés plus haut, nous allons maintenant voir comment auto permet d’itérer facilement sur des containers et de différentes manières :
Sortie console :
Il reste encore quelques exemples à explorer, comme les auto dans les paramètres des fonctions templatés, depuis le C++17 , avec ce petit exemple :
Sortie console quand un appel est fait la fonction : product<32,45>()
Derniers exemples possibles, en C++17, avec auto, des déclarations de liaison structurées, commençons par un tableau :
Ici, on déclare un simple tableau, en l’assignant ensuite à des variables automatiques, non référence et référence. Voici la sortie :
On constate effectivement que les qualificateurs de
variables, référence par exemple, sont appliqués aux déclarations de liaison
structurées.
Il existe deux autres exemples avec des tuples ou des membres de données d’une classe :
Et la sortie :
Apres ces séries d’exemples en tout genre, il est temps d’attaquer un petit paragraphe qui sert de conclusion.
Auto, quand l'utiliser ?
C’est ici que la plupart des développeurs C++ n’ont pas les mêmes avis. Certains préfèrent utiliser cette nouvelle utilisation avec parcimonie, typiquement dans des variables temporaires comme des itérateurs dans des boucles.
Du coté des professionnels du langage, comme Scott Meyers ou Herb Sutter, ou des entreprises comme Microsoft, ils sont plutôt avocats de l’utiliser quasi tout le temps. Que ce soit pour des valeurs de retour ou des récupérations de valeurs complexes afin de s’affranchir de la lecture technique d’une fonction.
Pour la plupart des développeurs, l’adaptation s’effectue surtout dans l’équipe selon les versions des compilateurs et de la volonté d’évolution du code existant.