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 :

variadic functions sans stdarg ?


Sujet :

Langage C++

  1. #1
    Membre très actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Par défaut variadic functions sans stdarg ?
    Bonjour tout le monde,

    voilà je me remet au C++ après des années de sommeil

    J'étais en train de faire des tests sur des variadic templates et je me suis demandé s'il y avait possibilité d'itérer plus "modernement" sur la liste des arguments d'une variadic function qu'avec les fonctions de la SL :

    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
     
    #include <vector>
     
    template <class T>
    class A
    {
        private :
            std::vector<T> v;
     
        public :
            A(T& ts...);       
    };
     
    template <class T>
    A<T>::A(T& ts...)
    {
        std::vector<T> temp = { ts... };
        for (auto t : temp)
            v.push_back(t);
    }
    Le compilateur me répond que je fais une mauvaise utilisation de "ts..." car, je cite :

    ||=== Build: Debug in POO (compiler: GNU GCC Compiler) ===|
    POO\main.cpp||In constructor 'A<T>::A(T&, ...)': |
    POO\main.cpp|32|error: expansion pattern 'ts' contains no argument packs|
    POO\main.cpp||In instantiation of 'A<T>::A(T&, ...) [with T = int]': |
    POO\main.cpp|40|required from here|
    main.cpp|32|error: could not convert '{<expression error>}' from '<brace-enclosed initializer list>' to 'std::vector<int, std::allocator<int> >'|
    ||=== Build failed: 2 error(s), 2 warning(s) (0 minute(s), 0 second(s)) ===|
    N'y a-t'il pas une manière plus ou moins aussi élégante de parcourir ce type de liste ?

    Merci d'avance

  2. #2
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Bonjour,

    Un code équivalent qui compile et fonctionne :
    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
    #include <vector>
     
    template<class T>
    class A
    {
        private :
            std::vector<T> v;
     
        public :
            template<class... Args>
            A(Args&&... args);       
    };
     
    template<class T>
    template<class... Args>
    A<T>::A(Args&&... args)
    {
        std::vector<T> temp = { std::forward<Args>(args)... };
        for(auto& t : temp)
            v.push_back(t);
    }
    Cela dit, il est inutile de créer un vecteur temporaire. Il vaut mieux écrire directement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<class T>
    template<class... Args>
    A<T>::A(Args&&... args) :
        v({ std::forward<Args>(args)... })
    {
    }
    Concernant les parameter packs, tu trouveras tous les détails ici : http://en.cppreference.com/w/cpp/lan...parameter_pack

  3. #3
    Membre très actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Par défaut
    oui pour le temp c'était à force de tenter des écritures pour voir d'où venait le problème.

    Merci pour les infos.

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    A quoi sert le std::forward dans ce code ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<class T>
    template<class... Args>
    A<T>::A(Args&&... args) :
        v({ std::forward<Args>(args)... })
    {
    }
    J'ai l'impression que ça fonctionne très bien sans. Merci

  5. #5
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Citation Envoyé par Bktero Voir le message
    A quoi sert le std::forward dans ce code ?
    C'est pour éviter des copies inutiles dans le cas où des arguments sont des rvalues. Si tu essaies le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct Test
    {
        Test() = default;
        Test(const Test&) {std::cout << "copie ";}
        Test(Test&&)      {std::cout << "deplacement ";}
    };
     
    int main()
    {
    	A<Test> monTest(Test{});
    	return 0;
    }
    Sans le std::forward, ça devrait t'afficher "copie copie". Avec le std::forward, ça devrait t'afficher "deplacement copie".

    De manière générale, voici un lien qui explique bien le fonctionnement et l'intérêt de std::forward :
    http://stackoverflow.com/questions/3...582313#3582313

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Bon... Je ne vois déjà pas où est appelé la première fois le constructeur par copie. cppreference m'a fait comprendre que Test {} provoque un appel au constructeur par copie, std::forward provoque l'appel au constructeur par déplacement. Quand je lis A(Args&&... args); et notamment le &&, dois-je comprendre que c'est un constructeur par déplacement ou pas du tout ?

    Pourquoi est-ce que le code suivant ne fait pas appel au constructeur par déplacement ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main()
    {
        Test test = Test { };
        A<Test> monTest(test);
        return 0;
    }
    Je m'en vais potasser ton lien

  7. #7
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    En fait, && n'est pas "déplacement" mais "référence sur une rvalue".

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main()
    {
        Test test = Test { };
        A<Test> monTest1(test);
        A<Test> monTest2( Test { } );
    }
    ici, monTest1 est construit via une Test const& (copie), tandis que monTest2 peut être construit via Test&&.
    Note que tu pourrais imaginer un troisième constructeur prenant un Test const&&, sans grand intérêt, toute fois.

    Le nom de toute variable est une lvalue reference sur sa valeur (ou pour être précis sa case mémoire). Normal, c'est le rôle premier d'une variable: nommer un bout de mémoire.
    Seule une valeur temporaire (telle un retour de fonction ou un appel à un constructeur) peut être liée à une référence sur rvalue.

    C'est là qu'intervient std::move en transformant une variable en rvalue, permettant de liée une && dessus.

    Notons que le problème a un corollaire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int f(Bidule && b) {
        utiliser( std::forward<Bidule>(b) );
    }
    Ici, l'appelant de f se débrouille comme il veut pour générer un b (std::move ou valeur temporaire).
    Par contre f se trouve avec une variable [b] de type Bidule &&. Cette variable ne peut pas être liée à une lvalue, quelle qu'elle soit.
    C'est ici qu'intervient std::forward, comme une sorte de cast de "variable de type &&" en "rvalue".

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

Discussions similaires

  1. printf sans stdarg.h
    Par lwhite dans le forum Débuter
    Réponses: 4
    Dernier message: 06/11/2008, 18h14
  2. Function sans Return
    Par Danyel dans le forum VB.NET
    Réponses: 5
    Dernier message: 02/05/2008, 17h10
  3. Réponses: 2
    Dernier message: 31/03/2008, 17h53
  4. Réponses: 3
    Dernier message: 28/09/2007, 11h44
  5. transport tablespace sans view function et package
    Par debdba dans le forum Administration
    Réponses: 13
    Dernier message: 25/06/2007, 11h58

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