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

SL & STL C++ Discussion :

Utiliser std::forward deux fois ?


Sujet :

SL & STL C++

  1. #1
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut Utiliser std::forward deux fois ?
    Bonjour,

    Je suis dans le cas (simplifié) suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template<typename F1, typename F2, typename ... Args>
    std::pair<std::function<void()>, std::function<void()>>
    do_bind(F1&& f1, F2&& f2, Args&& ... args) {
        return make_pair(
            std::bind(std::forward<F1>(f1), std::forward<Args>(args)...),
            std::bind(std::forward<F2>(f2), std::forward<Args>(args)...)
        );
    }
    Je ne maîtrise pas encore totalement la sémantique de mouvement, mais j'ai peur que ce code ne fonctionne pas comme je le voudrais : d'après moi, les arguments sont "déplacés" pour le premier bind, il ne sont alors plus disponibles pour le second.

    On peut le tester avec ce code :
    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
    struct A {
        int i = 0;
        A() = default;
        A(int i) : i(i) {}
        A(const A& a) : i(a.i) {}
        A(A&& a) : i(a.i) { a.i = 0; }
    };
     
    void some_func(const A& a) { std::cout << a.i << std::endl; }
    void some_other_func(const A& a) { std::cout << a.i << std::endl; }
     
    int main() {
        auto ret = do_bind(&some_func, &some_other_func, A(5));
        ret.first();
        ret.second();
    }
    ... qui affiche 0 et 5, suggérant que le second membre de la paire a été initialisé en premier.

    Ma question est donc :
    • que faut-il faire pour que les deux fonctions ainsi créées reçoivent toutes deux des arguments aussi bien "forwardés" que possible?
    • quels sont les inconvénients de cette approche :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      template<typename F1, typename F2, typename ... Args>
      std::pair<std::function<void()>, std::function<void()>>
      do_bind(F1&& f1, F2&& f2, Args&& ... args) {
          auto fb1 = std::bind(std::forward<F1>(f1), args...);
          auto fb2 = std::bind(std::forward<F2>(f2), std::forward<Args>(args)...);
          return make_pair(std::move(fb1), std::move(fb2));
      }


    Je précise qu'un des buts de cette fonction est d'éviter à son utilisateur d'avoir à fournir deux fois une même série d'arguments.

  2. #2
    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
    Bonjour ,

    Le problème dans ce cas c'est que bind stocke en interne les arguments args... pour les réutiliser plus tard lors de l'appel, et qu'il les stocke soit en les copiant, soit en les déplaçant selon qu'on lui passe une lvale ou une rvalue.

    Hors le but de std::forward c'est de prendre ll'expression qu'elle reçoit en paramètre et la retransformer correctement en lvalue ou rvalue, selon la manière dont on a appelé la fonction.

    Par exemple à ce stade do_bind(&some_func, &some_other_func, A(5)); l'expression A(5) est une rvalue et donc lorsqu'on fait ensuite std::bind(std::forward<F1>(f1), std::forward<Args>(args)...) std::forward va transormer l'arg en rvalue, et donc permettre std::bind de le stocker en le déplaçant

    On aurait le même problème dans ce cas par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void foo(A&& a)
    {
       A a1(std::forward<A>(a));
       A a2(std::forward<A>(a));
    }
     
    foo(A(5));
    Il me semble que le plus simple reste de ne pas forwarder du tout et tout passer par const ref. Après tout il faut bien que les deux bind copient les arguments pour pouvoir les réutilser plus tard donc on va perde l'info de lvaluness/rvalueness quoi qu'il arrive:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template<typename F1, typename F2, typename ... Args>
    std::pair<std::function<void()>, std::function<void()>>
    do_bind(F1&& f1, F2&& f2, const Args& ... args) {
        return make_pair(
            std::bind(std::forward<F1>(f1), args...),
            std::bind(std::forward<F2>(f2), args...)
        );
    }

  3. #3
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    Merci pour ta réponse. Ca confirme donc ce que je pensais.

    Citation Envoyé par Arzar Voir le message
    Il me semble que le plus simple reste de ne pas forwarder du tout et tout passer par const ref. Après tout il faut bien que les deux bind copient les arguments pour pouvoir les réutilser plus tard donc on va perde l'info de lvaluness/rvalueness quoi qu'il arrive:
    Oui, si on fait comme ça et qu'on utilise un temporaire, il sera copié pour chaque fonction. Mais avec le dernier code que je propose, on économise une copie, le dernier bind pouvant profiter de la sémantique de mouvement. Dans de nombreux cas, c'est une micro-optimisation, mais ça peut s'avérer intéressant (pourvu que ça ne créé pas d'autres problèmes, mais je n'en vois pas pour le moment).

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

Discussions similaires

  1. [2014] Table utilisée deux fois
    Par slim025 dans le forum MS SQL Server
    Réponses: 1
    Dernier message: 27/11/2014, 18h28
  2. Deux fois plus de ram utilisée que prévu
    Par noobjava dans le forum Windows
    Réponses: 1
    Dernier message: 05/10/2013, 16h14
  3. Utiliser deux fois le même script dans la même page
    Par atc666 dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 10/01/2012, 10h17
  4. Utiliser deux fois l'instruction LDS
    Par ned_kelly dans le forum x86 16-bits
    Réponses: 2
    Dernier message: 17/12/2010, 03h51
  5. MSCRM 4.0 : utiliser un champ deux fois dans un formulaire
    Par irid dans le forum Microsoft Dynamics CRM
    Réponses: 4
    Dernier message: 09/06/2010, 11h39

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