Publicité
+ Répondre à la discussion
Page 1 sur 2 12 DernièreDernière
Affichage des résultats 1 à 20 sur 24
  1. #1
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut Variadic template : ai-je loupé quelque chose ou est-ce un bug?

    Salut,

    En préparant une réponse à cette discussion, j'ai été confronté à un comportement qui me semble étrange de la part de gcc...

    En effet, si j'exécute le code
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    #include <string>
    #include <iostream>
    template<typename ...Args>
    void foo(Args ... args)
    {
        std::cout<<"stopping with "<<sizeof...(args)<< " unused arguments"<<std::endl;
    }
    template<typename ...Args>
    void foo(int i, Args ... args)
    {
        std::cout<<"using i = "<<i
                 <<", left "<<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    template<typename ... Args>
    void foo(std::string const & s, Args...args)
    {
        std::cout<<"using the "<<s<<" string, left "
                 <<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    int main()
    {
     
        foo<int, int,int, int, std::string , int, int >(1, 2, 3, 4,5 ,
                                                       "salut", 6, 7 );
        return 0;
    }
    l'exécution s'arrpête après avoir utiliser l'argument "salut", en me disant qu'il reste 2 arguments inutilisés.

    Par contre, si je rajoute une déclaration des fonctions sous la forme de
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #include <iostream>
    #include <string>
    template<typename ...Args>
    void foo(int i, Args ... args);
    template<typename ...Args>
    void foo(std::string const & s, Args...args);
    template<typename ...Args>
    void foo(Args ... args)
    {
        std::cout<<"stopping with "<<sizeof...(args)<< " unused arguments"<<std::endl;
    }
    template<typename ... Args>
    void foo(std::string const & s, Args...args)
    {
        std::cout<<"using the "<<s<<" string, left "
                 <<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    template<typename ...Args>
    void foo(int i, Args ... args)
    {
        std::cout<<"using i = "<<i
                 <<", left "<<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    int main()
    {
     
        foo<int, int,int, int, std::string , int, int >(1, 2, 3, 4,5 ,
                                                       "salut", 6, 7 );
        return 0;
    }
    l'exécution va jusqu'à son terme...

    Je me serais pourtant attendu, étant donné que la définition de fonction vaut déclaration, à ce que le premier code fonctionne correctement !!!

    Alors, selon vous, aurais-je loupé quelque chose, ou s'agit-il d'un bug de gcc
    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

  2. #2
    Membre chevronné
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    mars 2009
    Messages
    424
    Détails du profil
    Informations personnelles :
    Localisation : France

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

    Informations forums :
    Inscription : mars 2009
    Messages : 424
    Points : 695
    Points
    695

    Par défaut

    Ton premier code arrive à terme avec ceci en sortie avec gcc 4.6.3

    Code :
    1
    2
    3
    4
    5
    6
    7
     
    using i = 1, left 7 argument more
    using i = 2, left 6 argument more
    using i = 3, left 5 argument more
    using i = 4, left 4 argument more
    using i = 5, left 3 argument more
    stopping with 3 unused arguments

  3. #3
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut

    Citation Envoyé par bretus Voir le message
    Ton premier code arrive à terme avec ceci en sortie avec gcc 4.6.3

    Code :
    1
    2
    3
    4
    5
    6
    7
     
    using i = 1, left 7 argument more
    using i = 2, left 6 argument more
    using i = 3, left 5 argument more
    using i = 4, left 4 argument more
    using i = 5, left 3 argument more
    stopping with 3 unused arguments
    Ben oui, et c'est justement là le problème...

    Je me serais attendu à ce que le premier code fournisse exactement la même sortie que le second, vu que les surcharges représentent le "best match" à chaque fois

    Mais, avant d'envoyer un rapport de bug, j'aimerais savoir ce que vous en pensez, au cas où je serais passé à coté d'un détail

    (note que j'ai ce comportement que je crois erroné avec gcc 4.7.1 "build perso" )
    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
    Inactif


    Homme Profil pro Guillaume Belz
    Biochimiste
    Inscrit en
    novembre 2008
    Messages
    5 318
    Détails du profil
    Informations personnelles :
    Nom : Homme Guillaume Belz
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Biochimiste
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 318
    Points : 17 319
    Points
    17 319

    Par défaut

    Même résultat que Bretus : le programme s'arrête avant d'utiliser la spécialisation pour string, pas après. Donc résultat normale, la fonction spécialisée pour int ne peut pas appeler la spécialisation pour string (qui est déclarée après) et appelle donc la version non spécialisée (le stop)
    Donc pas de bug de notre côté (gcc 4.8 linux chez moi). Teste avec un build non perso (ou poste un draft pour modifier la norme C++ pour qu'elle respecte ton build perso ? )

  5. #5
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut

    Citation Envoyé par gbdivers Voir le message
    Même résultat que Bretus : le programme s'arrête avant d'utiliser la spécialisation pour string, pas après. Donc résultat normale, la fonction spécialisée pour int ne peut pas appeler la spécialisation pour string (qui est déclarée après) et appelle donc la version non spécialisée (le stop)
    Donc pas de bug de notre côté (gcc 4.8 linux chez moi). Teste avec un build non perso (ou poste un draft pour modifier la norme C++ pour qu'elle respecte ton build perso ? )
    En fait, je me doute bien que toutes les versions actuelles présentent le meme comportement, donc cela ne sert pas à grand chose de me confirmer que c'est celui que vous obtenez.

    Par contre, étant donné que tout est inline, il ne devrait pas y avoir une approche plus ou moins SFINAE qui ferait que les deux codes devraient réagir de la même manière

    Parce que l'on est quand même bien d'accord sur le fait que le compilateur commence à implémenter les fonctions quand il rencontre l'appel dans main.

    A ce moment là, il a déjà vu les définitions de toutes les surcharge et doit donc choisir le best match, même si la surcharge utilisant la std::string arrive après celle utilisant int.

    Autrement, cela veut dire que tu auras un comportement différent en fonction de l'ordre de tes paramètrex ou de l'ordre dans lequel tu définis tes surcharges, et ca, ca ne me semble pas normal du tout:

    Cela signifierait, avec le code tel qu'il est dans mon premier exemple, que si tu remplace l'appel par
    Code :
    1
    2
    foo<std::string , int, int,int, int, int, int >( "salut",1, 2, 3, 4,5 ,
                                                       6, 7 );
    tu verrais tous les arguments...

    Ca me semble quand meme un peu suspect, non
    C'est pour cela que je demande si j'ai loupé quelque chose
    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
    Inactif


    Homme Profil pro Guillaume Belz
    Biochimiste
    Inscrit en
    novembre 2008
    Messages
    5 318
    Détails du profil
    Informations personnelles :
    Nom : Homme Guillaume Belz
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Biochimiste
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 318
    Points : 17 319
    Points
    17 319

    Par défaut

    Ok, je comprend ce que tu veux dire... je pensais que tu disais que chez toi, le compilateur s'arrêtait après la spécialisation string, pas avant (donc un résultat différent de nous)
    Du coup, je sais pas trop

  7. #7
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut

    Pas de problème pour moi

    Ah, et, pour info, le "build perso", c'est parce que je me suis amusé à compiler une version "multilib" qui compile par défaut en bits, rien de plus
    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

  8. #8
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro Loïc Joly
    Développeur informatique
    Inscrit en
    août 2004
    Messages
    4 963
    Détails du profil
    Informations personnelles :
    Nom : Homme Loïc Joly
    Âge : 40
    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 : 4 963
    Points : 11 268
    Points
    11 268

    Par défaut

    Ce comportement me semble normal. Ce qui compte pour la recherche de noms, c'est le point de définition, pas le point d'instanciation (autrement, deux instanciations d'un même template avec les mêmes paramètres pourraient être différentes, ce qui ferait désordre...). Ton exemple me semble assez proche à celui du 14.6.3, le fait que les templates soient variadiques n'y change rien.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Et celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++

  9. #9
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut

    Citation Envoyé par JolyLoic Voir le message
    Ce comportement me semble normal. Ce qui compte pour la recherche de noms, c'est le point de définition, pas le point d'instanciation (autrement, deux instanciations d'un même template avec les mêmes paramètres pourraient être différentes, ce qui ferait désordre...). Ton exemple me semble assez proche à celui du 14.6.3, le fait que les templates soient variadiques n'y change rien.
    Cela peut sembler logique, sauf qu'en l'occurrence, ce n'est même pas le point de définition qui joue, mais le point de déclaration

    Et c'est ca qui m'inquiète le plus car cela signifie que l'ordre des déclarations puisse etre important :p

    Or , je viens de vérifier avec le code
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #include <iostream>
    #include <string>
    template<typename ...Args>
    void foo(Args ... args);
     
    template<typename ...Args>
    void foo(int i, Args ... args);
     
    template<typename ...Args>
    void foo(std::string const & s, Args...args);
    template<typename ...Args>
     
    void foo(Args ... args)
    {
        std::cout<<"stopping with "<<sizeof...(args)<< " unused arguments"<<std::endl;
    }
    template<typename ... Args>
    void foo(std::string const & s, Args...args)
    {
        std::cout<<"using the "<<s<<" string, left "
                 <<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    template<typename ...Args>
    void foo(int i, Args ... args)
    {
        std::cout<<"using i = "<<i
                 <<", left "<<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    int main()
    {
     
        foo<int, int,int, int, std::string , int, int >(1, 2, 3, 4,5 ,
                                                       "salut", 6, 7 );
        return 0;
    }
    et, indépendamment de la positon des définitions, l'ordre des déclarations est important, dans le sens où tous les éléments ne sont affichés que si la déclaration de la fonction ne prenant que le variadic se trouve en dernière position.

    Le problème, c'est que cela sous entend que, si tu venais à avoir les trois fonctions déclarées dans des fichiers différents, l'ordre d'inclusion des fichiers serait important même si tu n'as qu'un seul fichier d'implémentation.

    Et ca, ca reviendrait à peu près à dire que tu ne peux pas inclure <iostream> avant d'avoir inclus <string> .

    Je suis tout à fait prêt à te croire lorsque tu cite le paragraphe de la norme, mais, si c'est le seul qui entre en ligne de compte, alors, il y a effectivement peut etre un problème au niveau de la norme, parce que cela va à l'encontre du principe selon lequel l'ordre d'inclusion ne doit pas influer, sauf cas particulier

    Maintenant, je connais suffisamment la norme pour au moins savoir qu'il faut être particulièrement attentif en la lisant, car on a vite fait de passer sur un paragraphe important aussi

    Et c'est pour cela que je reste quand meme plus ou moins sceptique .


    Au temps pour moi, j'avais pas fait attention

    L'ordre de déclaration ne change rien, et on a le comportement attendu (tous les arguments affichés) quel que soit l'ordre de déclaration des fonctions.
    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

  10. #10
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    juin 2012
    Messages
    851
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : juin 2012
    Messages : 851
    Points : 1 598
    Points
    1 598

    Par défaut

    Pour info ton premier code (celui sans les déclarations préalables) utilise tous les arguments sous ICC.
    Donc l'un des deux compilos a un comportement erroné.
    Reste à savoir lequel ^^"

  11. #11
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut

    Citation Envoyé par Iradrille Voir le message
    Pour info ton premier code (celui sans les déclarations préalables) utilise tous les arguments sous ICC.
    Donc l'un des deux compilos a un comportement erroné.
    Reste à savoir lequel ^^"
    Ben, à vrai dire, c'est à ce genre de comportement que je me serais attendu :p
    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

  12. #12
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut

    Pour info, dans le doute, j'ai introduit un rapport de bug... nous verrons ce que l'équipe de dev en pense

    Je passe le sujet en résolu, mais n'hésitez pas à intervenir pour la cause
    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 Confirmé

    Avatar de germinolegrand
    Homme Profil pro Germino Legrand
    Développeur de jeux vidéo
    Inscrit en
    octobre 2010
    Messages
    731
    Détails du profil
    Informations personnelles :
    Nom : Homme Germino Legrand
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : octobre 2010
    Messages : 731
    Points : 3 939
    Points
    3 939

    Par défaut

    Pour avoir pas mal bossé avec les variadics, j'ai le regret de te dire que ton code dans bugzilla a strictement le comportement attendu, un autre comportement serait illégal et me surprendrait énormément ^^.
    Il est le résultat du SFINAE, à la 6e itération, tu demandes explicitement comme premier type template un int, mais tu demandes à ce que le premier argument soit une std::string. Comme le compilo ne connait pas de spécialisation
    Code :
    1
    2
    template<class ... Args>
    foo<int, Args...>(std::string, Args ... args);
    il applique le SFINAE et va chercher la seule signature correcte, c'est à dire
    Code :
    1
    2
    template<class ... Args>
    foo<Args...>(Args ... args);
    Choisis un travail que tu aimes et tu n'auras pas à travailler un seul jour de ta vie.

    N'oubliez pas de marquer votre sujet comme et de mettre des aux messages apportant un plus à votre discussion.

    Si vous souhaitez participer à la rubrique C++, ne me contactez plus !

  14. #14
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut

    Tu sembles avoir loupé la spécialisation qui prend une std::string en premier parametre...
    Le compilateur devrait (SFINAE aidant) choisir
    Code :
    1
    2
    3
    4
    5
    6
    7
    template<typename ...Args>
    void foo(int i, Args ... args)
    {
        std::cout<<"using i = "<<i
                 <<", left "<<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    pour les valeurs 1 à 7 et devrait retomber sur
    Code :
    1
    2
    3
    4
    5
    6
    7
    template<typename ...Args>
    void foo(std::string const & s, Args...args)
    {
        std::cout<<"using the "<<s<<" string, left "
                 <<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    pour "hello", non?

    Note que je ne spécialise jamais le template, je me contente de surcharger la fonction

    [EDIT]Pour autant que j'aie bien compris, quelle que soit la manière dont j'appelle la fonction à la base, il devrait dérouler les paramètres variadiques sous une forme proche de
    1. int, int,int, int, std::string , int, int==> (int, Args...)
    2. int,int, int, std::string , int, int==> (int, Args...)
    3. int, int, std::string , int, int==> (int, Args...)
    4. int, std::string , int, int==> (int, Args...)
    5. std::string , int, int==> (std::string, Args...)
    6. int, int==> (int, Args...)
    7. int==> (int, Args...)
    8. (rien)==> (Args...)
    Comme il n'y a aucune spécialisation de la fonction, mais seulement des surcharges, lorsqu'il arrive à std::string comme premier paramètre, ce devrait être la surcharge adéquate ( foo( std :: string const &, ...args) ) qui devrait etre appelée, surtout à cause du SFINAE.

    Et comme le fait remarquer gbdivers, ton raisonnement semble cohérent, mais alors, comment expliquerions nous que la simple déclaration "anticipée" de la fonction fasse réagir le code différemment
    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

  15. #15
    Inactif


    Homme Profil pro Guillaume Belz
    Biochimiste
    Inscrit en
    novembre 2008
    Messages
    5 318
    Détails du profil
    Informations personnelles :
    Nom : Homme Guillaume Belz
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Biochimiste
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 318
    Points : 17 319
    Points
    17 319

    Par défaut

    ça a l'air crédible comme explication
    Par contre, pourquoi la déclaration anticipée permet de résoudre le problème ?

  16. #16
    Expert Confirmé Avatar de Flob90
    Homme Profil pro Florian Blanchet
    Etudiant en Optique
    Inscrit en
    août 2004
    Messages
    1 225
    Détails du profil
    Informations personnelles :
    Nom : Homme Florian Blanchet
    Âge : 24
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Etudiant en Optique

    Informations forums :
    Inscription : août 2004
    Messages : 1 225
    Points : 2 528
    Points
    2 528

    Par défaut

    Loic a donner une explication, elle me semble correcte aussi (surtout appuyer par l'exemple de la norme auquel il fait référence) :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    void g(double);
    void h();
     
    template<class T> class Z {
      public:
        void f() {
         g(1); // calls g(double)
         h++; // ill-formed: cannot increment function;
                 // this could be diagnosed either here or
                 // at the point of instantiation
        }
    };
     
    void g(int); // not in scope at the point of the template
                      // definition, not considered for the call g(1)
    De la même manière dans ton code, il est à chaque fois dans l'instanciation du "deuxième" template, donc il ne connait pas le "troisième" et il appel donc une instanciation du "premier" quand il ne peut plus utiliser une instanciation du deuxième".

    Quant tu fais une déclaration de tes fonctions template, lorsqu'il est dans une instance du "deuxième" template, le compilateur utilise les trois noms pour appliquer l'algo de sélection.

    Ca peut sembler restrictif dans le sens où les PI peuvent être localisés de manière à "voir" les trois noms, cependant comme la norme ne fixe pas totalement les PI, imposer les comportements par rapport aux définitions et pas aux PI évite d'introduire des UB de partout.

    @Koala: Tu devrais virer la liste explicite de paramètre template de ton appel à foo, ca évitera d'introduire des problèmes supplémentaire (ie mal compté les types par rapport au pramètre). Normalement le compilo déduit tout seul.
    "We can solve any problem by introducing an extra level of indirection" Butler Lampson

    "N'importe quel problème peut être résolu en introduisant un niveau d'indirection supplémentaire" Butler Lampson (traduction libre)

  17. #17
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 691
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 691
    Points : 15 768
    Points
    15 768

    Par défaut

    Si ce n'est que SFINAE permet, justement de "descendre" dans les définitions...

    Pour rappel : SFINAE = Single Failure Is Not An Error, donc, avec le code que j'ai donné dés le départ (et que j'ai copié sur bugzilla), à savoir :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    #include <string>
    #include <iostream>
    template<typename ...Args>
    void foo(Args ... args)
    {
        std::cout<<"stopping with "<<sizeof...(args)<< " unused arguments"<<std::endl;
    }
    template<typename ...Args>
    void foo(int i, Args ... args)
    {
        std::cout<<"using i = "<<i
                 <<", left "<<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    template<typename ... Args>
    void foo(std::string const & s, Args...args)
    {
        std::cout<<"using the "<<s<<" string, left "
                 <<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    int main()
    {
     
        foo<int, int,int, int, std::string , int, int >(1, 2, 3, 4,5 ,
                                                       "salut", 6, 7 );
        return 0;
    }
    il commence par avoir un int, qu'il gère comme tel, et "un nombre inconnus de paramètres dont il ignore, et il utilise la fonction foo(int i, Args ... args)

    Pour les paramètres 2, 3, 4 et 5, c'est le meme principe

    Puis il arrive à
    "salut", 6, 7, ce qui, de son coté signifie "une chaine de caractères et une série de paramètre dont il ignore tout"...

    Ca tombe bien, il a justement une surcharge de la fonction qui permet cela sous la forme de
    Code :
    1
    2
    3
    4
    5
    6
    7
    template<typename ... Args>
    void foo(std::string const & s, Args...args)
    {
        std::cout<<"using the "<<s<<" string, left "
                 <<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    Il devrait donc l'utiliser, au lieu de tomber sur le cas de base

    Et, enfin, il devrait se retrouver avec les deux derniers entiers, reconnus comme tels, et terminer la boucle, pour ne tomber dans le cas de base que... lorsqu'il n'y a plus d'argument à traiter.

    Ce qui me fait dire qu'il y a réellement un problème n'est pas tant le fait que le comportement final va dépendre des déclarations de fonctions (quoi que...), mais bien le fait que, si tu change un tout petit peu l'ordre de définition des fonctions, en définissant
    Code :
    1
    2
    3
    4
    5
    6
    7
    template<typename ... Args>
    void foo(std::string const & s, Args...args)
    {
        std::cout<<"using the "<<s<<" string, left "
                 <<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    avant de définir
    Code :
    1
    2
    3
    4
    5
    6
    7
    template<typename ...Args>
    void foo(int i, Args ... args)
    {
        std::cout<<"using i = "<<i
                 <<", left "<<sizeof...(args)<<" argument more"<<std::endl;
        foo(args...);
    }
    tu auras un comportement différent, alors que l'ordre d'implémentation ne devrait pas avoir d'impact...

    Je suis tout prêt à accepter vos explications et peut etre que le problème est "ailleurs", mais, pour l'instant on se heurte à un problème qui ne devrait pas arriver : le fait que l'ordre d'implémentation de fonction au niveau d l'unité de compilation puisse changer le comportement de l'application
    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

  18. #18
    Expert Confirmé Avatar de Flob90
    Homme Profil pro Florian Blanchet
    Etudiant en Optique
    Inscrit en
    août 2004
    Messages
    1 225
    Détails du profil
    Informations personnelles :
    Nom : Homme Florian Blanchet
    Âge : 24
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Etudiant en Optique

    Informations forums :
    Inscription : août 2004
    Messages : 1 225
    Points : 2 528
    Points
    2 528

    Par défaut

    Je viens de regarder de plus près, j'ai quelques pistes, mais rien de bien certains.

    Premièrement le passage que cite Loic n'est pas vraiment applicable dans ce cas, ce passage fait référence au noms non-dépendant, or f(arg...) est une expression qui dépend des paramètres template.

    Dans ce cas le lookup est faite au moment de l'instanciation depuis, à la fois, la définition et le POI.

    Ce qui fait que selon la localisation des POI la spécialisation nécessaire fera ou non parti du set (le SFINAE n'intervient que pour inclure ou non une spécialisation dans le set, il ne permet pas de voir plus "loin").

    D'autre part la norme spécifie (ODR) qui si deux POI donnent deux comportements différent, alors le programme est mauvais mais que le compilateur n'a pas à le signaler (dans la pratique le compilateur doit prendre un POI parmis tout ceux sélectionner, d'où un résultat "aléatoire"). Néanmoins, il semblerait que les compilateurs utilisent souvent des POI en fin de TU, donc ce problème part quelque peu (ca n'en reste pas moins que le programme est faux).

    Un dernier point est que le lookup effectué depuis le POI est ADL, donc même si le POI est à la fin, si tes arguments ne sont pas de type déclaré dans le même scope que les fonctions (c'est pas exactement ca, mais le sujet n'est pas l'ADL), les seules trouvées sont celles vue depuis la définition (lookup non ADL).

    Illustrons tout ca :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    #include<iostream>
     
    namespace N
    {
     
    class C {};
     
    //typedef C A;
    //Compile, permet d'avoir l'ADL
    //typedef int A;
    //Ne compile pas, ne permet pas l'ADL
     
    class B {};
     
    //template<class... Arg>
    //void g(A,Arg...);
    //Les deux compilent avec ceci
    //Permet d'être visible depuis la définition
    //Pas besoin d'ADL
     
    void g()
    { }
     
    template<class... Arg>
    void g(B,Arg... arg) 
    { 
    	std::cout << 0;
    	g(arg...);
    }
     
    template<class... Arg>
    void g(A,Arg...)
    { std::cout << 1; }
     
    }
     
    int main()
    {
    //	g(N::B(),N::A());
    // Compile dans certains cas, cf les autres commentaires
    // Besoin de l'ADL ou d'être visible depuis la définition
    //	g(N::B(),N::A(),N::B());
    // Compile tout le temps
    // Le dernier paramètre assure l'ADL
    }
     
    //Le POI des instanciations est surment ici, donc il "voit" tout
    "We can solve any problem by introducing an extra level of indirection" Butler Lampson

    "N'importe quel problème peut être résolu en introduisant un niveau d'indirection supplémentaire" Butler Lampson (traduction libre)

  19. #19
    Membre Expert

    Inscrit en
    mai 2008
    Messages
    1 007
    Détails du profil
    Informations forums :
    Inscription : mai 2008
    Messages : 1 007
    Points : 1 958
    Points
    1 958

    Par défaut

    Citation Envoyé par Iradrille Voir le message
    Pour info ton premier code (celui sans les déclarations préalables) utilise tous les arguments sous ICC.
    Donc l'un des deux compilos a un comportement erroné."
    On peut ajouter que Visual Studio 2012 + CTP affiche lui aussi tous les arguments avec le premier code.
    Edit : Par contre clang se comporte comme GCC.

  20. #20
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro Loïc Joly
    Développeur informatique
    Inscrit en
    août 2004
    Messages
    4 963
    Détails du profil
    Informations personnelles :
    Nom : Homme Loïc Joly
    Âge : 40
    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 : 4 963
    Points : 11 268
    Points
    11 268

    Par défaut

    Citation Envoyé par koala01 Voir le message
    si tu change un tout petit peu l'ordre de définition des fonctions, en définissant [...] tu auras un comportement différent, alors que l'ordre d'implémentation ne devrait pas avoir d'impact...

    Je suis tout prêt à accepter vos explications et peut etre que le problème est "ailleurs", mais, pour l'instant on se heurte à un problème qui ne devrait pas arriver : le fait que l'ordre d'implémentation de fonction au niveau d l'unité de compilation puisse changer le comportement de l'application
    Je pense que c'est là que tu fais fausse route. L'ordre de définition a un impact. Même sans template, sans variadique, ni rien :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include <iostream>
    using namespace std;
     
    int f1(int i) { return 0; }
     
    int g1() {return f1('a'); }
     
    int f1(char a) { return 1; }
     
    //---------
     
    int f2(int i) { return 0; }
     
    int f2(char a) { return 1; }
     
    int g2() {return f2('a'); }
     
    //---------
     
    int main()
    {
    	cout << g1() << " " << g2() << endl;
    }
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Et celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •