Les nouveaux standards à partir C++ 11 apportent énormément de fonctionnalités, que ça soit
dans le langage lui-même ou bien dans la bibliothèque standard. A tel point que cela peut être
compliqué de suivre toutes les nouveautés, c’est pourquoi j’écris cet article, afin de faciliter
cet apprentissage étape par étape.

C++11 feels like a new language: The pieces just fit together better than they used to and I
find a higher-level style of programming more natural than before and as efficient as ever.
(Bjarne Stroustrup)


Je vais donc vous parler des mots clefs auto et decltype, apparus en C++11, et vous montrer
leurs significations, ce à quoi ils servent, et comment bien les utiliser.
auto existait avant C++ 11 mais sa signification a été modifiée car il n’était pas utilisé car
inutile.

Auto : déduire le type de variables
Commençons par le mot clef auto : c’est celui des deux avec le plus de champs d’application,
et par conséquent celui que vous verrez et utiliserez probablement le plus.
Auto permet tout d’abord de faire de la déduction de type. Ici, avec le premier exemple, le
compilateur déduit tout seul que i est de type int parce que 5 est un entier, j est un int car
l’addition d’un int avec un int renvoie un int, et str est une std::string parce que
function_returning_a_string() retourne une std::string.

std::string function_returning_a_string();
void auto_simple_example()
{
auto i = 5; // int i;
auto j = i + 10; // int j;
auto str = function_returning_a_string(); // std::string str
}

A noter que la déduction a lieu à la compilation ! Au niveau de l’exécutable généré il n’y a
aucune différence entre « int i = 5 » et « auto i = 5»

Auto : type de retour de fonction
L’autre utilité du mot clef auto est de pouvoir l’écrire à la place du type de retour d’une
fonction, pour le renseigner après les arguments, avec une flèche :

auto function_with_trailing_return() -> int
{
return 5;
}

Petite amélioration qui arrive en C++ 14, le type peut être déduit automatiquement comme
ceci :

// Works only with C++14
auto function_with_deduced_returned_type()
{
return 5; // Return an int
}

Decltype : détection de types simples
Maintenant, passons au mot clef decltype : il permet d’avoir le type d’une variable, d’une
fonction ou bien d’une expression. Dans l’exemple de droite, j’ai un entier i : si je souhaite
déclarer une variable j du même type que i, je peux utiliser decltype(i) pour renseigner le type
de j. Il me permet également, par exemple, de déclarer une variable h qui soit du type du
résultat de «i + j».

int i = 1;
decltype(i) j = i; // int j
decltype(i + j) h = i + j; // int h

Sur des entiers, l’utilisation n’a pas l’air très utile, mais on aurait pu imaginer vouloir
connaitre le type de retour d’une addition entre une std::string et un const char*, qui renvoie
une std::string.

const char* c_string = "C++ is cool."
std::string std_str = "C++11 is even better.";
decltype(std_str + c_string) result = std_str + c_string;

Decltype : détection de types complexes
Vous avez sûrement encore du mal à voir l’utilité de ce mot-clef pour l’instant. Prenons un
exemple que ce mot-clef permet de simplifier. Voyez la fonction ci-dessous : elle prend en
paramètre une callback, fait des calculs, puis appelle la callback prise en paramètre et retourne
la valeur renvoyée par la callback, le tout sans connaître le type de retour de la callback prise
en paramètre ! Avec decltype, on peut connaître facilement ce que retourne la «cb() ».

template <typename Callback>
auto more_advanced_example(Callback cb) -> decltype(cb())
{
// Do some calculation
return cb();
}

Dans l’exemple, j’ai utilisé le mot-clef auto pour pouvoir mettre le type de retour à droite, car
si je l’avais mis à gauche, je n’aurais pas pu utiliser l’argument « cb » car il n’aurait pas été
encore déclaré.
Certains se demanderont peut-être comment faire si la callback était censée prendre des
arguments, et bien c’est presque pareil :

template <typename Callback>
auto more_advanced_example(Callback cb, int i, int j) -> decltype(cb(i,j))
{
// Do some calculation
return cb(i, j);
}

Decltype : son fidèle compagnon std::declval
Autre problématique qui peut arriver : il est possible que vous n’ayez pas de variable du bon
type sous la main pour pouvoir créer des expressions dont le type serait déduit. Par exemple,
si je voulais ne pas mettre mon type de retour à droite après la flèche, cette notation ne
marcherait pas car ni cb, ni i, ni j ne sont déclarés à ce moment-là. Pour pallier à ce problème,
il y a la fonction std::declval : elle permet d’obtenir une variable de n’importe quel type pour
être utilisée dans des expressions de déduction de type.

template <typename Callback>
decltype(std::declval<Callback>()()) declval_example(Callback cb)
{
return cb();
}

Ne jamais utiliser std::declval pour autre chose que déduire un autre type avec decltype.
En C++14, on aurait pu aussi utiliser le mot clef auto pour déduire tout seul le type de retour
de la fonction comme ceci :
template <typename Callback>
auto auto_is_powerfull(Callback cb, int i, int j)
{
return cb(i, j);
}

Différence entre détection et déduction de type
Revenons un moment sur mot-clef auto. Comme je l’ai dit précédemment, auto déduit le type
alors que decltype permet de détecter le type. Voyons donc un exemple où la différence se
fait voir :

std::vector<std::string> strings {"auto", "and", "decltype"};
auto str = strings[0]; // std::string str
decltype(strings[0]) str_ref = strings[0]; // std::string& str_ref;

L’opérateur [] d’un std::vector renvoie une référence. Pourtant, avec auto on obtient une std: :string et non une std::string&amp;, alors qu’avec decltype, on obtient bien une std ::string&amp;.
La ligne avec decltype semble bien verbeuse, pour pallier à ça, on peut soit :

  • Ajouter soi-même la référence avec le mot clef auto, comme ceci :
1 auto& str_ref = strings[0];

  • Ou utiliser – en C++14 - la syntaxe « decltype(auto) », comme ceci :
1 decltype(auto) str_ref = strings[0];

Il reste une dernière possibilité du mot clef auto pour déduire tout seul les arguments d’une
fonction lambda, mais j’en parlerai ultérieurement, dans un article dédié aux lambdas.

Quelques domaines d’application
Maintenant, voyons quelques-uns des champs d’application les plus courants pour ces mots-
clefs.

Les types longs peu importants
Les types longs à écrire dont le type est assez explicite même sans le marquer, comme des
itérateurs retournés par les fonction « begin » / « cbegin »:

std::vector<int> ints {0, 1, 2, 3, 4};
for (std::vector<int>::const_iterator it = ints.cbegin(); it !=
ints.cend(); ++it)
;
for (auto it = ints.cbegin(); it != ints.cend(); ++it)
;

Je ne sais pas vous, mais moi je trouve que la seconde ligne est bien plus lisible.
Les types évidents
Quand le type n’est pas important dans l’expression, ou bien qu’il est évident :

auto int_ptr = std::make_unique<int>(5); // std::unique_ptr<int> int_ptr
auto my_pair = std::make_pair(5, 10); // std::pair<int, int> my_pair

Stocker une lambda

Sachant qu’il est impossible de connaitre le vrai type d’une lambda (car généré par le
compilateur) , pour le stocker, soit on peut utiliser auto, soit on peut le stocker dans des
wrappers tels que std::function (ce qui a un léger coût par contre).

Le code générique/utilisant des templates
Dans du code totalement générique, comme par exemple les fonctions prenant en paramètres
des callbacks comme présentées plus haut dans l’article, puisque l’on veut des éléments qui
peuvent prendre une multitude de types ayant les bonnes propriétés, auto et decltype sont des
vrais atouts.

La métaprogrammation
En métaprogrammation, qui se base énormément sur le fait de jouer avec les types et leurs
propriétés, decltype est un outil presque indispensable (mais la métaprogrammation reste, il
est vrai, un domaine de niche en C++).

Les abus à éviter
Attention à ne pas abuser du mot clef auto ! Si on le met partout, le code peut vite devenir
difficile à lire, cela doit donc rester du sucre syntaxique et, comme le sucre dans la vraie vie,
c’est à prendre avec modération.

Attention également à ne pas utiliser decltype là où c’est inutile, cela risquerait d’ajouter de la
verbosité sans apporter la flexibilité que ce mot-clef peut apporter dans certains cas.

Les mots-clefs auto et decltype peuvent, dans certains cas, vous simplifier la vie et vous
permettre d’écrire du code plus clair et plus concis, à condition de ne pas en abuser.

Ils vous permettent aussi d’écrire du code très générique en se liant très bien aux templates.
De plus, ils se marient très bien avec les nouvelles fonctionnalités arrivées en C++11 telles
que les lambdas.

by Lena Bertho

Si vous souhaitez découvrir nos opportunités professionnelles, c'est par ici : https://www.elephant-technologies.fr/carriere/opportunites