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 :

Retourner un objet non-copiable


Sujet :

Langage C++

  1. #1
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut Retourner un objet non-copiable
    Bonjour à tous,

    La situation est très simple : j'ai un objet que je souhaite non-copiable, comment faire pour faire retourner un tel objet par une fonction ?
    Évidemment, je souhaite que cet objet soit alloué sur la pile, sinon ce serait trop simple

    Dans la pratique, pour peu que le compilateur utilise la NRVO, il ne se produit aucune opération de copie. Comment expliciter ce mécanisme ?

    Je pensais à un truc comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    void
    function(a_class& object_to_be_returned);
    mais c'est un peu moche et pas très expressif. En tout cas j'ai jamais rien vu de tel dans Boost (par exemple).

    Si vous avez une idée à base de rvalue references (ou toute autre nouveauté C++0x), je suis également preneur .
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  2. #2
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Si tu peux rendre ton objet déplaçable, mais non copiable, alors effectivement, les rvalue references me paraissent être la solution.
    Ne connaissant pas encore la syntaxe exacte, je laisse à d'autres le soin de la fournir.

    Sinon, il te faudra allouer par défaut ton objet dans un état valide, mais non exploitable, dans le code appelant et la passer en référence à la fonction qui va alors le modifier.

    Sinon, il te reste la sémantique bâtarde du COW, et autres auto_ptr<> (qui vont impliquer une allocation sur le tas)
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 176
    Points
    1 176
    Par défaut
    ben j'utilise ça tout le temps si je ne veux pas de copie. Genre pour remplir un vector.

  4. #4
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    Si tu peux rendre ton objet déplaçable, mais non copiable, alors effectivement, les rvalue references me paraissent être la solution.
    Ne connaissant pas encore la syntaxe exacte, je laisse à d'autres le soin de la fournir.
    Justement, j'ai lu plein de fois « il parait que c'est possible », mais je n'ai jamais vu le code qui était censé le faire


    Sinon, il te faudra allouer par défaut ton objet dans un état valide, mais non exploitable, dans le code appelant et la passer en référence à la fonction qui va alors le modifier.

    Sinon, il te reste la sémantique bâtarde du COW, et autres auto_ptr<> (qui vont impliquer une allocation sur le tas)
    Pour ces solutions là, ce qui m'embête c'est que j'aimerais avoir une syntaxe expressive et intuitive (donc pas bâtarde). C'est que ces fameuses fonctions feront partie de l'API de ma lib…
    EDIT : D'autant plus qu'auto_ptr est déprécié dans C++0x.


    Pour l'instant, je vais creuser encore un peu la piste rvalue reference.
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  5. #5
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par Florian Goo Voir le message
    Si vous avez une idée à base de rvalue references (ou toute autre nouveauté C++0x), je suis également preneur .
    Alors là, je suis perplexe. Il y a quelques temps dans le thread sur les rvalue reference c'est toi-même, florian qui avait posté une des réponses les plus pertinentes sur ce sujet

    Donc, oui, avec les rvalue reference c'est assez simple, il faut rajouter un constructeur par déplacement et un opérateur d'affectation par déplacement (faudrait vraiment qu'on est une traduction "officielle" un jour ).

    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
    20
    21
    22
    23
     
    class NonCopyable
    {
        public:
       NonCopyable(){cout << "Default Constructor\n";}
       ~NonCopyable(){cout << "Destructor\n";}
       NonCopyable(const NonCopyable& ) = delete;
       NonCopyable& operator=(const NonCopyable& ) =  delete;
       NonCopyable(NonCopyable&&){cout << "Move Constructor\n";}
       NonCopyable& operator=(NonCopyable&&){ cout << "Move assignment\n"; return *this;}
    };
     
    NonCopyable foo()
    {
        NonCopyable localncp; // alloué dans la pile
        return localncp;
    }
     
    int main()
    {
        NonCopyable ncp = foo();
        return 0;
    }
    Affichage avec NRVO :
    DefaultConstructor

    Affichage sans NRVO
    Default Constructor (création de localncp dans la pile de foo())
    Move Constructor (création du temporaire retourné par foo(), par déplacement de localncp)
    Destructor (destruction de localncp en sortant de foo())
    Move Constructor (creation de ncp dans main() par déplacement du temporaire)
    Destructor (destruction du temporaire)
    Destructor (destruction de ncp, en sortant du main())

  6. #6
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Ce ne sera pas plutôt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    NonCopyable && foo()
    {
        NonCopyable localncp; // alloué dans la pile
        return std::move(localncp);
    }
    la syntaxe ?

    Après je n'ai pas suivi l'affaire au sujet du RVO/NRVO au milieu de tout ça.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  7. #7
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Justement non !
    J'ai fait exactement la même erreur en essayant la première fois. Pour comprendre d'où vient le problème il faut en fait désactiver la NRVO et détailler ce qui se passe dans le cas normal.

    Avec un objet classique copiable, le retour d'une fonction se fait comme suit:
    (1) création de localcp
    (2) création d'un Copyable temporaire par copie qui va faire le lien entre foo() et main()
    (3) on sort de la portée de foo(), le destructeur de localcp est appelé.
    (4) création de cp par copie du temporaire.
    (5) Le destructeur du temporaire est appelé.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Copyalbe foo()
    {
      Copyable localcp; // (1)
      return localcp; // (2)
    } // (3)
     
    int main()
    {
       Copyable cp = foo(); (4)
      ; // (5)
    }
    Affichage NRVO
    Default constructor // une seule construction. L'optimisation a fait complètement sauter le temporaire.

    Affichage sans NRVO
    Default Constructor
    Copy Constructor // (2)
    Destructor // (3)
    Copy Constructor // (4)
    Destructor // (5)

    Et si l'on décortique le code faux, avec le move:
    (1) création de localncp
    (2) c'est ici l'erreur. move(localncp) convertit localncp en rvalue reference, c'est à dire en temporaire et donc le return ne va pas créer de temporaire car il considère que localcp en est déjà un.
    EDIT: Correction, l'erreur ne vient pas du move mais du type de retour NonCopyable&&. C'est à cause de lui qu'il n'y a pas création de temporaire.
    (3) localncp est détruit en sortant de la portée comme d'habitude.
    (4) boom.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    NonCopyable&& foo()
    {
      NonCopyable localncp; // (1)
      return move(localncp); // (2)
    } // (3)
     
    int main()
    {
       NonCopyable ncp = foo(); // (4)
    }

  8. #8
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Alors là, je suis perplexe. Il y a quelques temps dans le thread sur les rvalue reference c'est toi-même, florian qui avait posté une des réponses les plus pertinentes sur ce sujet
    Ouais, je me souviens de ce topic, mais là pour le coup ça m'aurait arrangé qu'on me contredise, que quelqu'un me sorte une solution tout droit sortie d'un chapeau magique .

    C'est tout de même une limitation du langage assez gênante que voici .
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  9. #9
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Quelle limitation ? Je ne comprends pas. L'ajout d'un move constructeur comme décrit plus haut semble être une réponse élégante au problème initial non ?

    Citation Envoyé par Florain Goo
    J'ai un objet que je souhaite non-copiable, comment faire pour faire retourner un tel objet par une fonction ?
    Évidemment, je souhaite que cet objet soit alloué sur la pile, sinon ce serait trop simple

  10. #10
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Toutes mes excuses, j'avais lu ton post tellement vite que je suis complètement passé à côté de son contenu

    Malgré mon apparente maitrise du sujet, je n'avais absolument pas saisi le principe du move constructor qui intervient à la place du copy constructor lorsque ce dernier est delete (ou private) dans le contexte d'un retour de fonction.

    Effectivement, c'est en effet très bien fichu
    Je viens de tester et ça m'a l'air de fonctionner à merveille.

    Merci de mettre ça sur le compte de la fatigue

    Merci beaucoup
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  11. #11
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Les astuces d'émulation de sémantiques de mouvement en C++03 se basent d'ailleurs sur la NRVO.
    Voir Boost.Move.
    Boost ftw

  12. #12
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    Citation Envoyé par Arzar Voir le message
    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
    20
    21
    22
    23
     
    class NonCopyable
    {
        public:
       NonCopyable(){cout << "Default Constructor\n";}
       ~NonCopyable(){cout << "Destructor\n";}
       NonCopyable(const NonCopyable& ) = delete;
       NonCopyable& operator=(const NonCopyable& ) =  delete;
       NonCopyable(NonCopyable&&){cout << "Move Constructor\n";}
       NonCopyable& operator=(NonCopyable&&){ cout << "Move assignment\n"; return *this;}
    };
     
    NonCopyable foo()
    {
        NonCopyable localncp; // alloué dans la pile
        return localncp;
    }
     
    int main()
    {
        NonCopyable ncp = foo();
        return 0;
    }
    C'est du C++0x je suppose, je n'ai jamais vu cette synthaxe

  13. #13
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Tout à fait !
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

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

Discussions similaires

  1. [Débutant] Retourné un objet non primitif?
    Par Cube55 dans le forum CORBA
    Réponses: 4
    Dernier message: 12/02/2009, 23h34
  2. Objets Non copiables et conteneur
    Par CedricMocquillon dans le forum C++
    Réponses: 10
    Dernier message: 04/12/2008, 15h01
  3. [OBJET] - non-aggregate type error
    Par jacquesh dans le forum C++
    Réponses: 3
    Dernier message: 28/04/2006, 13h49
  4. [gcc/ld] comment "zapper" les objets non référéren
    Par jula dans le forum Autres éditeurs
    Réponses: 5
    Dernier message: 05/01/2006, 15h15
  5. [POO] balise ou objet non reconnu lors d'un deploiement serveur
    Par benssj5 dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 02/01/2006, 17h26

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