IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Référence vers littéral


Sujet :

Langage C++

  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut Référence vers littéral
    Hello,

    Je viens de découvrir qu'on pouvait faire :

    Quel intérêt ?

    Le seul que je vois, c'est l'utilisation des références dans les appels de fonction, pour éviter de copier un argument qu'on donne sous forme d'objet temporaire. Dans ce cas là, l'intérêt de la chose serait lié aux temporaires, plutôt qu'aux littéraux...

  2. #2
    Membre Expert Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Par défaut
    Bonjour

    Lorsque tu as une fonction qui prend une référence sur membre constant, tu peux passer un objet ou un temporaire directement.

    Personnellement, je m'en sers pour "renommer" des variables.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::map<key_t, value_t> map;
    for (auto const & key_value : map)
    {
        key_t const & key = key_value.first;
        value_t const & value = key_value.second;
    }

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    En fait, ce n'est ni plus ni moins qu'un "effet de bord" des restrictions imposées aux références : une référence, qu'elle soit constante ou non, doit être initialisée au moment de sa déclaration, point barre.

    Du moment où l'initialisation de la référence est correcte, le compilateur s'en fout royalement de savoir si cela a du sens ou non! Il n'est pas payé pour cela et il n'a aucun moyen de le décider.

    Il agit donc en "brave petit soldat" qui ne demande même pas "à quelle hauteur" quand on lui demande de sauter, et qui saute aussi haut et aussi loin que possible .

    Alors, est-ce que ca a du sens Pas vraiment.

    Mais c'est toi qui prend les décisions de conception et si tu prends une décision absurde, mais syntaxiquement correcte, le compilateur ne pourra que te répondre "oui chef, bien chef" parce que c'est tout ce qu'on lui a apris à faire
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    MA question ne se situait pas vraiment à propos du compilateur.

    Mais j'ai lu ça dans C++ Primer, et ça m'avait intrigué.

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par oodini Voir le message
    MA question ne se situait pas vraiment à propos du compilateur.
    Je me doutes bien, mais comme c'est lui qui dira si le code est valide ou non, tout passe par lui et, comme il ne fait qu'appliquer les règles "simples" qu'on lui a indiqué, bah... il n'a pas trop le choix.

    Mais, sinon, il faut savoir que déclarer une référence constante n'est pas anormal en soi :

    Si tu dois travailler avec un objet renvoyé par l'accesseur d'un objet constant (le patron de conception dit "du null object" permet de renvoyer systématiquement une référence, y compris sur un objet invalide, par exemple), le code est très certainement plus lisible si tu écris un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    const MonType & recup=obj.getXXX();
    recup.truc();
    recup.brol();
    recup.bidule();
    que si tu dois commencer à passer systématiquement par l'accesseur de l'objet, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    obj.getXXX().truc();
    obj.getXXX().brol();
    obj.getXXX().bidule();
    (sans compter que tu y gagne une indirection )

    Dans de telles conditions, il est même tout à fait normal, logique et légal de récupérer une référence constante

    Mais, à partir du moment où il est légal de le faire avec un objet ayant sémantique d'entité, il est légal de le faire avec strictement n'importe quoi

    Et c'est là qu'apparait l'effet de bord : quand tu utilises un littéral, la valeur de celui-ci est placée en mémoire et prend la place de n'importe quelle valeur de type similaire, donc, tu peux en récupérer l'adresse ou définir une référence constante dessus.

    Alors, comme je te le disais, pour un type primitif, cela n'a pas vraiment beaucoup de sens, de le faire.

    Mais, comme ce n'est pas illégal (et, qu'en plus, cela un comportement cohérent du fait qu'on parle d'une référence constante), il n'y a rien qui t'interdise de le faire!

    Mais les développeurs font tellement de choses qui n'ont pas forcément beaucoup de sens! Ce n'est qu'une chose en plus finalement
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    int f(int const& i)
    {
      return i+1;
    }
     
    int g(int& i)
    {
      return i+1;
    }
     
    int main()
    {
       int a = 23;
       f(a); // OK
       f(42); // OK
       g(a); // OK
       g(42); // NOK
    }
    De la même manière :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    int&a = 42; // NOK
    int const&b = 42; // OK
    C’est une question de cohérence. Pouvoir passer une constante littérale (ou éventuellement un temporaire) à une fonction qui prend une référence constante est quand même bien pratique.

    Et n’oublie pas que la durée de vie d’un objet sur lequel existe une référence constante… se prolonge jusqu’à la fin de cette référence :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    std::string const& s = "toto"; // OK
    std::string & s2 = "toto"; // NOK

  7. #7
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Bon, j'ai reçu ce matin mon livre C++ Concurrency in Action (au bout d'un mois, il était temps).

    Et dans l'appendice A dévolu aux nouveautés de C++11, l'auteur répond explicitement à la question ayant initié ce fil de discussion :

    Normalement, les références (&) ne peuvent s'appliquer qu'à des L-values, pas à des R-values :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int& i=42;    // ne compile pas
    En revanche, ceci compile :

    C'est une exception délibérée au standard C++98 afin de passer des temporaires aux fonctions prenant des références.

    Ma supposition était donc bonne. Je comprend également que maintenant qu'il existe les références aux R-values (&&), cette exception n'a plus lieu d'être, mais qu'on la conserve par souci de rétrocompatibilité.

  8. #8
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 394
    Par défaut
    En fait, elle a toujours lieu d'être, car on veut toujours pouvoir passer des temporaires aux fonctions prenant leur paramètre par référence constante. Ce n'est pas une question de rétrocompatibilité, juste une question de praticité.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    La gestion de ces temporaires ne devrait-elle pas aujourd'hui plutôt se faire par une version de la fonction implémentée via la sémantique de mouvement (&&), maintenant qu'elle est disponible ?

  10. #10
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 394
    Par défaut
    Dupliquer la fonction juste pour avoir une version && ne sert à rien. Il faut qu'il y ait un "plus", comme le remplacement d'une copie par un mouvement, pour qu'il y ait un intérêt.

    La sémantique de mouvement, pour moi, c'est surtout que selon moi pratiquement chaque classe non-copiable devrait avoir un move-constructor, qui lui permette d'être retournée par une fonction.

    C'est surtout à ça que ça sert pour moi, en fait: permettre de retourner des objets non-copiables (typiquement, objets gérant un fichier ou autre ressource système) d'une fonction, et optimiser le retour d'objets "lourds" (conteneurs, etc.)
    Pour le reste, à part dans un constructeur ou un setter/une fonction d'ajout dans un conteneur, j'ai du mal à voir l'intérêt d'un paramètre &&.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  11. #11
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    @Médinoc: Pour l'intérêt du &&, il sert aussi lors de l'écriture de fonctions génériques :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template<class T>
    void foo(T&& t) //T en C++03 (*)
    {
      bar(t); //bar modifie t
      goo(forward<T>(t));
    }
     
    //(*) Ou 2 versions dont l'une appel l'autre en optimisant un peu
    @oodini: Si tu n'as pas besoin de modifier l'objet passé en paramètre, devoir faire 2 versions serait tout simplement redondant. Un const T& convient parfaitement

  12. #12
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    En fait, il faut comprendre qu'une référence constante te permet de fournir ce que l'on appelle une "variable temporaire anonyme" comme argument à ta fonction.

    Par exemple, la fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void foo(std::string const & str){
        /* ... */
    }
    peut aussi bien être appelée sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void bar(){
        std::string str("salut");
        /* manipulation de str */
        foo(str);
    }
    que sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void bar2(){
        foo("salut le monde");
    }
    L'appel dans bar va "simplement" éviter la copie de str et éviter que la chaine de caractères ne soit induement modifiée dans foo, l'appel dans bar2 va créer une "variable temporaire anonyme".

    Comme chaque terme compte :
    • c'est une valeur temporaire : qui ne doit exister que le temps de l'appel à la fonction
    • c'et une valeur anonyme : on ne donne pas de nom à cette valeur en dehors de la fonction appelée, ce qui fait qu'il n'y a pas moyen de l'utiliser dans la fonction appelante.
    Tu n'as, à ce moment là, absolument pas besoin de la sémantique de mouvement! Tu as juste besoin d'une valeur, potentiellement arbitraire (comme peut l'être la chaine "salut le monde" dans foo2), qui puisse être considérée (grâce aux opérateurs de conversions et ou à un constructeur particulier) comme étant du type attendu par la fonction (une std::string dans le cas présent).

    Comme cette valeur n'existera que durant la durée de l'appel de foo et qu'elle sera absolument irrécupérable dans la fonction appelée, il n'y a absolument pas lieu de permettre à la fonction foo de la modifier.

    La sémantique de mouvement n'a strictement rien à voir là dedans

    Maintenant, il est "logique" que, si l'on admet la création d'une variable temporaire anonyme pour la transmission de paramètres, on admette également la construction d'une telle variable -- sous réserve bien sûr d'appliquer les même restriction -- de manière générale.

    Encore une fois, il n'y a pas forcément de sens ni d'intérêt à le faire, mais les restrictions imposées font que ton code restera parfaitement valide et "sécurisé" (comprends : tu ne cours absolument aucun risque à le faire).

    Il faut comprendre que la sémantique de mouvement a été essentiellement envisagée afin de faciliter (généraliser ) des comportements du type NRVO chaque fois que c'était possible, voire, comme l'a fait remarquer Médinoc, pour faciliter la tâche avec des objets qui, par nature, ne devraient pas être copiables.

    Notes d'ailleurs que l'idée d'utiliser la sémantique de mouvement sur des objets non copiables me laisse un tantinet perplexe, si du moins on part du principe qu'un objet non copiable aura sémantique d'entité

    Je n'ai rien contre le fait de l'utiliser tant que l'objet (ayant sémantique d'entité) n'est pas référencé par ailleurs (en gros : au sortir d'une fabrique quelconque ou de tout comportement similaire).

    Mais une fois qu'il est référencé par ailleurs, on garde bien sur l'unicité référentielle (une seule instance de l'objet présentant strictement les mêmes valeurs, y compris celles intervenant dans l'identification unique et non ambigües), mais toutes les références existant avant le mouvement seront invalidées, ce qui risque de provoquer des problèmes aussi surement que si l'on avait invoqué delete sur un pointeur vers l'objet
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  13. #13
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 394
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Notes d'ailleurs que l'idée d'utiliser la sémantique de mouvement sur des objets non copiables me laisse un tantinet perplexe, si du moins on part du principe qu'un objet non copiable aura sémantique d'entité

    Je n'ai rien contre le fait de l'utiliser tant que l'objet (ayant sémantique d'entité) n'est pas référencé par ailleurs (en gros : au sortir d'une fabrique quelconque ou de tout comportement similaire).

    Mais une fois qu'il est référencé par ailleurs, on garde bien sur l'unicité référentielle (une seule instance de l'objet présentant strictement les mêmes valeurs, y compris celles intervenant dans l'identification unique et non ambigües), mais toutes les références existant avant le mouvement seront invalidées, ce qui risque de provoquer des problèmes aussi surement que si l'on avait invoqué delete sur un pointeur vers l'objet
    En fait, comme je l'avais dit c'est surtout pour les pointeurs intelligents (genre unique_ptr) et handles intelligents, qui, bien que référençant des entités, ont eux-mêmes une sémantique de valeur.

    Bien sur, dans la plupart des cas on pourrait y substituer un comptage de référence (shared_ptr etc.) mais cela peut être inutilement coûteux quand une sémantique de transfert suffit (je suppose que c'est pour les mêmes raisons qu'on n'utilise pas systématiquement shared_ptr, et qu'on a inventé unique_ptr).

    Je dois aussi admettre que j'ai un préjugé contre le comptage de références non-intrusif: Coûteux en performance et en localité mémoire, viole la règle du "une ressource bète par objet", etc.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  14. #14
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Je dois aussi admettre que j'ai un préjugé contre le comptage de références non-intrusif: Coûteux en performance et en localité mémoire, viole la règle du "une ressource bète par objet", etc.
    Moi, mon préjugé est contre l'intrusif (le fait de gérer un objet par ref count ou pas dépend pour moi plus de l'usage qu'on veut en faire que de son type).
    Maintenant, je serais curieux de voir où le non intrusif par shared_ptr a un cout (en perf ou en localité mémoire) par rapport à l'intrusif, quand il est géré à l'aide de make_shared.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  15. #15
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 394
    Par défaut
    <consulte doc make_shared()>
    En effet, make_shared() semble résoudre tous ces problèmes d'un coup.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 05/04/2008, 16h07
  2. [JSTL] références vers le site de sun
    Par nikalkal dans le forum Taglibs
    Réponses: 7
    Dernier message: 17/12/2007, 15h38
  3. Garder une référence vers une variable d'une autre classe
    Par choupeo dans le forum Windows Forms
    Réponses: 5
    Dernier message: 08/12/2007, 18h30
  4. Utilisations croisés de références vers des dll
    Par Pilloutou dans le forum Framework .NET
    Réponses: 3
    Dernier message: 05/11/2006, 21h56
  5. Comment stocker une référence vers un TTreeNode ds un DFM ?
    Par phplive dans le forum Composants VCL
    Réponses: 1
    Dernier message: 19/07/2005, 12h33

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo