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 template : ai-je loupé quelque chose ou est-ce un bug?


Sujet :

Langage C++

  1. #1
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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 : 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
    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 : 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
    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 éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Ton premier code arrive à terme avec ceci en sortie avec gcc 4.6.3

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    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
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    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
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    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
    Points : 16 213
    Points
    16 213
    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.
    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.

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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 : 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
    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
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    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
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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
    Membre expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    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 : 738
    Points : 3 892
    Points
    3 892
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<class ... Args>
    foo<Args...>(Args ... args);

  14. #14
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    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
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    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 : 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
     
    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.

  17. #17
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    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 : 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
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    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 : 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
    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

  19. #19
    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 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
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    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
    Points : 16 213
    Points
    16 213
    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 : 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
    #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.
    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.

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

Discussions similaires

  1. Oh mince ! Quelque chose s'est mal passé.
    Par FoX_*D i E* dans le forum Debian
    Réponses: 3
    Dernier message: 29/10/2013, 19h06
  2. Template, oublirais-je quelque chose?
    Par koala01 dans le forum Langage
    Réponses: 8
    Dernier message: 28/08/2013, 14h41
  3. Boucles imbriquées - j'ai loupé quelque chose ?
    Par senacle dans le forum Général Python
    Réponses: 2
    Dernier message: 10/12/2007, 11h22
  4. Bug ? Ou quelque chose s'est passé sur mon ordi ?
    Par souviron34 dans le forum Mode d'emploi & aide aux nouveaux
    Réponses: 3
    Dernier message: 06/09/2007, 17h36
  5. Réponses: 3
    Dernier message: 27/04/2004, 19h21

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