C++, auto et decltype

Ou comment mieux comprendre ces deux nouveautés

a marqué ce sujet comme résolu.

Il y a en faite trois catégories pour les expressions en c++,

  • lvalue qui désignent un objet ou une fonction;
  • xvalue qui désignent un objet qui devrait approcher sa fin de vie;
  • prvalue le reste

Les rvalue sont les xvalue et les prvalue ensemble. En général les traiter indifféremment n'a aucun impact, sauf pour le 'decltype' si c'est une xvalue alors on ajoute '&&' au type, et pour le traitement du type de e, si x c'est une prvalue on enlève l'éventuel premier 'const/volatile'.

Pour le terme référence universelle préfère forward reference qui a été retenu pour la norme.

Si tu veux jeter un oeil à la norme (un brouillon en accès libre) sans être totalement perdu, regardes spécifiquement:

  • 3.10 §1 catégories des expressions
  • 5 §5-6 ajustement du type des expressions
  • 7.1.6.2 §4 règles de 'decltype'
  • 7.1.6.4 §7 règles de 'auto'
  • 14.8.2.1 §2-3-4 déduction des argumemts génériques

Après une loooooongue période d’inactivité, j’ai décidé de reprendre la rédaction de ce tutoriel, en prenant toutes les remarques de Freedom en compte. Comme quoi, tes remarques ne sont pas tombées dans l’oublis, tu as vraiment bien fait de les écrire.

J’ai réécris l’introduction et la première partie sur les règles de déduction des templates, pour rendre la règle plus générique et plus claire à comprendre.

Merci d’avance pour vos commentaires.

Plop,

Je trouve étrange de parler d’exception dans la déduction d’un type plein, alors que c’est le comportement naturel pour le passage de paramètre: une copie n’a de qualificateurs qu’à la condition d’en mettre explicitement.

Idem pour les rvalues. En expliquant ce qui se passe dans une accumulation de référence on se retrouve sans exceptions: & + && = & (T1 = int&; T2 = T1&& = int&).

Il manque les const dans les commentaires du chapitre "Les rvalues references".

2 trucs pour savoir le type d’un template sans passé par boost:

  • __PRETTY_FUNCTION__
  • Une erreur de compilation avec un code comme template<class> S{}; /*...*/ S<T>{} = 1;. Je l’utilise régulièrement, principalement dans les tests de vérifier de type (S<decltype(f(...))>{} = S<int&>{}).
+1 -0

Merci pour ton retour jo_link_noir. J’ai réécris la partie, ça rend effectivement mieux avec une règle formulée sans exception.

Aurais-tu des ressources sur l’accumulation de références ? J’ai compris le principe mais je n’arrive pas à l’expliquer de manière simple et claire. Merci d’avance.

Pas de ressources, je crois bien avoir inventé quelque chose (yééé… \o/)

Dans l’idée je vois la chose comme ci-dessous:

  • le passage de paramètres n’existe que sous forme de référence (lvalue ou rvalue)¹. C’est important puisque que pour int * p; foo(p), ParamType = int * &.
  • à moins de le préciser, les modificateurs sont ignorés (const, volatile, &, &&).
  • les parties explicitement citées sont "soustraites" de T:
  • T & avec param = int &. On enlève & -> T = int.
  • T && avec param = int &. Pas de commun, T = int &, la fonction attend int & &&, la lvalue prévaut, la rvalue est enlevée.
  • T && avec param = int &&. On enlève && -> T = int.
  • T & avec param = int &&. Pas de commun, T = int &&. la fonction attend int && &, la lvalue prévaut, la rvalue est enlevée (= int &). La fonction attend une lvalue, le paramètre est une rvalue = la fonction ne peut pas matcher, c’est ok, on est bien sur une erreur.
  • T const & avec param = int &&. Pas de commun, T = int &&. la fonction attend int && const &, la lvalue prévaut, la rvalue est enlevée (param attendu: int const &, T = int (on enlève aussi la rvalue)). le paramètre passé (int &&) peut être convertie en int const &, c’est ok.

Pour faire une règle générale, il faut synthétiser les étapes du dernier points qui sont aussi valides pour les précédents.

  • Éliminer les modificateurs communs entre type passé et T.
  • S’il y a une référence du genre & &&/&& & dans le paramètre attendu, enlever && du paramètre attendu et de T (il peut ne pas y en avoir dans T).
  • Vérifier que la fonction est toujours appelable, sinon ce n’est pas une surcharge valide.

¹ Une autre idée est de considérer toutes expression comme une rvalue lors de la création et qu’y attacher une variable les transforment en lvalue. Une expression qui retourne une lvalue serait en fait une rvalue sur une lvalue simplifié en lvalue :p.

Merci jo_link_noir (bien que ton pseudo me rappellera toujours la galère du Temple de l’eau dans OoT) pour tes explications, le concept est bien plus clair dans ma tête maintenant. Cette règle, ça ne serait pas la reference collapsing rule ?

En attendant, j’ai fini de réécrire la première partie, n’hésitez pas à me faire des retours.

Merci d’avance pour vos commentaires.

Cette règle, ça ne serait pas la reference collapsing rule ?

C’est exactement ça, il faut que je retienne le nom pour une prochaine fois.

En cherchant la règle sur cppreference, on trouve un petit paragraphe. Comme quoi, c’est plus simple avec les bons mots :). Du coup, "accumulation de référence" donne une traduction inversée, ce n’est peut-être pas une bonne idée.

La réécriture avance bien, je pense que ça sera fini cette semaine (bon même si j’avais visé mercredi, soit). J’ai réécris la partie sur decltype, bien qu’elle ne soit pas terminée, ainsi que la partie sur quand les utiliser. Il ne reste plus qu’à finir la partie sur decltype avec notamment les prvalues, mettre la partie Connaître le type exact à jour, écrire la conclusion et relire.

Merci d’avance pour vos commentaires.

Voilà la version finale ! Maintenant, il reste quand même à faire des corrections, notamment sur la langue. Également, n’hésitez pas à commenter si des passages semblent peu clairs, erronés ou bien contenant des fautes de français.

Ah oui, il faut que je trouve un logo aussi. On verra ça plus tard.

Ce sujet est verrouillé.