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 :

Constructeur modèle sans argument, comment l’appeler ?


Sujet :

Langage C++

  1. #1
    Membre habitué Avatar de nowahn
    Homme Profil pro
    Inscrit en
    Août 2008
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 84
    Points : 150
    Points
    150
    Par défaut Constructeur modèle sans argument, comment l’appeler ?
    Bonjour,

    Premièrement, j’utilise le compilateur gcc/g++ version 4.5.0 sur Linux.

    En explorant le monde merveilleux des modèles (template), je suis tombé sur un os :
    on peut définir des fonctions modèles sans arguments (de fonction) et les appeler comme ceci :

    Code C++ : 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
    // Déclaration
    template<typename _Type>
    void Function();
     
    // Définition
    template<typename _Type>
    void Function()
    {
       std::cout << "void Function<_Type>()\n";
    }
     
    // Instanciation explicite
    template
    void Function<int>();
     
    // Appel
    int main()
    {
       Function<int>();
       return 0;
    }

    Par contre, j’ai remarqué qu’on pouvait déclarer, définir et instancier explicitement des constructeurs modèles sans arguments (de fonction) de la même manière, mais je suis incapable de les appeler :

    Code C++ : 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
    // Définition d’une classe
    class TClass
    {
    public:
       template<typename _Type>
       TClass();
    };
     
    // Définition du constructeur modèle
    template<typename _Type>
    TClass::TClass()
    {
       std::cout << "TClass::TClass<_Type>()\n";
    }
     
    // Instanciation explicite
    template
    TClass::TClass<int>(); // Ceci est légal
     
    // Utilisation
    int main()
    {
       TClass Class1; // Illégal, le constructeur par défaut n’est pas généré à
                      // cause de la présence du constructeur modèle (ça me semble
                      // normal)
     
       TClass Class2<int>; // Illégal, le '<' est pris pour un opérateur
                           // 'inférieur à'
     
       TClass Class3(TClass()); // Légal, le constructeur par copie par défaut est
                                // utilisé, ce qui me semble normal, mais le
                                // constructeur par défaut est généré ici alors
                                // qu’il ne l’est pas pour Class1, bizarre !!!
     
       TClass Class4(TClass<int>()); // Illégal, 'TClass<int>' est pris pour le
                                     // nom d’une classe modèle
     
       TClass Class5(TClass::TClass<int>()); // Illégal, un constructeur ne peut
                                             // pas être appelé directement
     
       return 0;
    }

    Pour info, voici les messages d’erreur renvoyés par g++ :
    Pour Class1 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Test.cpp:25:11: error: no matching function for call to ‘TClass::TClass()’
    Test.cpp:5:1: note: candidate is: TClass::TClass(const TClass&)
    Pour Class2 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Test.cpp:29:17: error: expected initializer before ‘<’ token
    Pour Class4 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Test.cpp:37:18: error: ‘TClass’ is not a template
    Pour Class5 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Test.cpp:40:38: error: cannot call constructor ‘TClass::TClass’ directly
    Test.cpp:40:38: error:   for a function-style cast, remove the redundant ‘::TClass’
    Test.cpp:40:38: error: no matching function for call to ‘TClass::TClass()’
    Test.cpp:5:1: note: candidate is: TClass::TClass(const TClass&)
    Notez que :
    • J’ai essayer de ne laisser qu’une seule de ces 5 déclarations à la fois et j’ai bien les mêmes messages d’erreur.
    • Lorsque je ne laisse que Class3, que je compile et que j’exécute, c’est bien le constructeur généré par défaut que s’exécute, pas le constructeur modèle avec un paramètre quelconque (pas de sortie sur la sortie standard).
    • Lorsque je mets la déclaration de Class1 après celle de Class3, le constructeur par défaut n’est toujours pas généré pour Class1.
    • J’ai bien compris quelques subtilités des modèles, comme le besoin du mot clé template dans certains cas pour appeler une fonction modèle sans arguments (de fonction), du genre de ce qui est expliqué sur la FAQ de comeau.
      J’ai donc essayé de rajouter ce mot clé à différents endroits, mais rien n’est légal.


    Bref, je dois passer à côté de quelque chose, car il me semble bizarre qu’on puisse déclarer, définir et instancier explicitement une fonction modèle qu’on ne peut pas appeler.

    1. Quelqu’un sait comment appeler ce constructeur modèle ?
    2. Est-ce que le comportement de g++ est conforme à la norme ?
    3. Est-ce que d’autre compilateurs acceptent une de ces syntaxes d’appel (je n’en ai pas sous la main) ?
    4. Est-ce que la génération du constructeur par défaut dans un cas et pas dans l’autre (Class1 et Class3) est correct ou est-ce un bug de g++ ?


    PS : ceci est une question purement technique, je n’ai pas d’exemple d’utilisation pratique en vue, c’est par pure curiosité que je la pose.

  2. #2
    Membre éprouvé Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Points : 997
    Points
    997
    Par défaut
    Hello !
    J'avoue que je suis aussi perplexe…
    Reste à voir si l'on peut trouver des cas d'application…

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    <tout le blabla d'avant>

    En fait, il n'y a, tout simplement, aucune raison de vouloir créer un constructeur template si c'est pour ne pas utiliser le l'argument template en question

    Je m'explique:

    Le constructeur a pour but... de créer une instance "correctement initialisée" de ta classe.

    Si toute ta classe est déclarée template, il est "normal" que le constructeur s'adapte en fonction de l'argument template (ne serait ce parce qu'il est fort possible que le type soit utilisé par ailleurs )

    De même, il est "cohérent", si tu veux passer un (des) argument(s) au constructeur, de faire en sorte qu'ils s'adaptent à un maximum de types.

    Mais si c'est pour avoir le constrcteur... par défaut, quelle serait l'utilité
    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
    Invité
    Invité(e)
    Par défaut
    Pour pouvoir passer le type aux constructeur peut-être. Un peu comme on pourrait avoir une classe qui à un type asssocie un gestionnaire, du genre :

    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
     
    class MaClasse
    {
             std::pair< std::type_info*, std::function<void()> > maPaire;
    public:
             template<class T>
             MaClasse(std::function<void()> maFonction)
             :maPaire( &typeid(T), maFonction )
             {}
     
             // Avec des fonctions membre de ce genre:
     
             template<class T>
             bool estAttachéACeType() const
             {
                     return (*maPaire.first) == typeid(T);
             }
    };
    Enfin c'est quand meme moins **problématique** que l'impossibilité de créer des version templates spécialisé de fonction (in peut d'ailleurs résoudre les deux problèmes de la même manière, avec une structure du genre Type2Type). Une limitation réelle des template que je trouve beaucoup plus contraignante, c'est l'impossibilité de les marier avec les fonctions virtuelles...

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Si ce n'est que là, tu passe, effectivement, un paramètre templatisé à ton constructeur, et ca, c'est tout à fait légal...

    une classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MaClass
    {
        public:
            template<typename iterator>
            MaClass(iterator b, iterator e):tab(b,e){}
            MaClass():tab(){}
        private:
            std::vector<int> tab;
    };
    fonctionne parfaitement, et pourra être utilisé en transmettant un écart compris entre deux itérateurs compatible (excluant std::map, en quelque sortes )

    Tu profitera d'ailleurs même de la déduction automatique des types

    Mais ce que nowahn essaye de faire, c'est de templatiser... le constructeur ne prenant pas d'argument, ce qui n'a strictement aucun sens: que veux tu spécialiser en terme de type au niveau du constructeur, si ce ne sont pas les arguments qui seront transmis
    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
    Invité
    Invité(e)
    Par défaut
    Sans vouloir te contredire, dans mon exemple (qui est invalide), le constructeur ne prend justement pas de paramètre pouvant permettre de determiner le type de T. Par contre je suis d'accord que c'est vraiment chercher la petite bête...

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Joe Dralliam Voir le message
    Sans vouloir te contredire, dans mon exemple (qui est invalide), le constructeur ne prend justement pas de paramètre pouvant permettre de determiner le type de T. Par contre je suis d'accord que c'est vraiment chercher la petite bête...
    Heuu...

    Visiblement, tu as "zappé" les deux lignes qui étaient les plus importantes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            template<typename iterator>
            MaClass(iterator b, iterator e):tab(b,e){}
    C'est ce constructeur qui permet de déterminer automatiquement le type: ici, comme je l'ai dit, il permet de déterminer automatiquement le type de l'itérateur utilisé

    Si tu ne me crois pas, essaye simplement:
    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
    int main()
    {
        MaClass c1; // appel du constructeur par défaut
        std::set<int> set;
        std::list<int> list;
        std::vector<int> vect;
        int tab[1000];
        for (int i=1;i<1000;++i) // je remplis tout 
        {
            set.insert(i);
            list.push_back(i);
            vect.push_back(i);
            tab[i]=1;
        }
        MaClass cSet(set.begin(),set.end()); // ca marche pour les std::set :D
        MaClass cList(list.begin(),list.end()); // pour les std::list
        MaClass cvect(vect.begin(),vect.end()); // pour les std::vector
        MaClass ctab(&tab[0], &tab[1000]); // et même pour les tableaux C style
    }
    évidemment, faut pas oublier d'inclure les fichiers requis par les différentes collection

    [EDIT]Et c'est tellement valide que c'est même utilisé comme constructeur (prévu par la norme) pour la plupart des collections de la STL
    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
    Membre habitué Avatar de nowahn
    Homme Profil pro
    Inscrit en
    Août 2008
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 84
    Points : 150
    Points
    150
    Par défaut
    Re,

    Tout d’abord, je ne cherche à rien faire, je me suis juste posé la question de savoir si c’était possible, après avoir chercher un moment la syntaxe d’un cas qui n’était pas trivial (un cas où il faut la syntaxe du type Machin::template Machin).
    Je ne vois pas à quoi un constructeur template sans arguments (de fonction) pourrait servir en pratique, mais je peux facilement imaginer des exemples sans intérêt mais tout à fait fonctionnels (enfin ça serait fonctionnel si il y avait un moyen de l’appeler ... ).

    Ce que je trouve bizarre au final, c’est qu’on puisse déclarer, définir et instancier explicitement quelque chose (ici un constructeur template sans arguments), mais pas l’appeler.
    Et accessoirement, le comportement de g++ qui génère une fois le constructeur par défaut et l’autre fois dit qu’il n’existe pas dans le même programme et pour la même classe me semble être un bug.

    Bref, comme je l’ai dit, ma question est purement technique et syntaxique (qui a dit tordue ? ... il a bien raison ). Ne vous inquiétez pas, je n’ai pas un client qui attend un programme qui dépende d’un tel constructeur pour demain matin.

    Et si vous voulez autre chose de tordu, sachez qu’il est par exemple possible de templatiser l’opérateur d’affection par copie :
    Code C++ : 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>
     
    // Une classe
    class TClass
    {
    public:
       template<typename _Type>
       TClass& operator=(TClass const& Src);
    };
     
    // Définition de l’opérateur modèle
    template<typename _Type>
    TClass& TClass::operator=(TClass const& Src)
    {
       std::cout << "TClass& TClass::operator=<_Type>(TClass const& Src)\n";
       return *this;
    }
     
    // Utilisation
    int main()
    {
       TClass Class1;
       TClass Class2;
     
       // L’opérateur d’affection par copie « normal » (celui dont parle la
       // norme) est toujours généré automatiquement
       Class1=Class2; // Rien ne s’affiche
     
       // On peut aussi appeler cet opérateur par la syntaxe style fonction
       Class1.operator=(Class2); // N’affiche toujours rien
     
       // Cette dernière syntaxe est la seule manière d’appeler notre opérateur
       // modèle
       Class1.operator=<int>(Class2); // Affiche le message
     
       return 0;
    }
    Ne me demandez pas l’intérêt d’un tel opérateur, je n’en vois pas, je dis simplement que tout ce code est légal (en tout cas pour g++), mais au moins dans ce cas, c’est cohérent, on peut appeler quelque chose qu’on peut définir.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    En fait, il me semble plus cohérent de pouvoir templatiser l'opérateur d'affectation que de vouloir templatiser un constructeur trivial...

    Le constructeur trivial n'a, tout bonnement, aucun intérêt à être templatisé, pour la simple et bonne raison qu'il n'y a strictement rien qui permette de profiter du paradigme générique.

    Par contre, dés qu'il prend un (ou plusieurs) paramètres, la templatisation du constructeur peut prendre de l'intérêt, car on peut profiter du paradigme générique

    Il ne faut, en outre, pas oublier que, à moins de le qualifier explicitement de explicit, un constructeur prenant un seul argument peut être vu comme un opérateur de conversion implicite:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MaClass
    {
        public:
            MaClass(int i){}
    };
    void foo(MaClass const & c){std::cout<<"foo"<<std::endl;}
    int main()
    {
        int i=152;
        foo(i); /* une variable anonyme temporaire sera créée grace au
                 * constructeur qui converti implicitement l'int en MaClass
                 */
    }
    La templatisation du constructeur permettrait donc de s'assurer, dans des circonstances identiques, que tout type qui sera compatible avec l'usage qui en sera fait par le constructeur pourrait être implicitement converti en MaClass

    L'opérateur d'affectation permet, au final, un comportement finalement fort similaire, car tu pourrais parfaitement envisager d'avoir
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MaClass
    {
        pubic:
            /* constructeur trivial */
            MaClass(){}
            /* constructeur servant d'opérateur de conversion d'entiers */
            MaClass(int i){}
            /* opérateur d'affectation "classique" */
            MaClass & operator=(MaClass const & rhs){/*...*/ return *this;}
            /* opérateur d'affectation permettant d'écire MaClass c; c=1; */
            MaClass & operator=(int i){/*...*/ return *this;}
    };
    Dés lors, pourquoi ne pourrait on pas templatiser ce que je montre pour un int

    Au fait, une note de bas de page de la norme nous dit:
    Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration
    of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors,
    and a template constructor may be used to copy an object if it provides a better match than other constructors.
    Traduction rapide:
    Parce qu'un constructeur template n'est jamais un constructeur par copie, la présence d'un tel template ne supprime pas la déclaratio implicite d'un constructeur par copie.

    Les constructeurs template participent à la résolution de la surcharge avec les autres constructeurs, incluant les constructeurs par copie, et un constructeur templatisé peut être utilisé pour copier un objet si il fournit une meilleur correspondance que les autres.
    [EDIT]on trouve d'ailleur une note de bas de page exactement similaire au sujet des opérateurs d'affectation templatisés
    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
    Invité
    Invité(e)
    Par défaut
    Je reviens à la charge pour dire que mon précédent message n'a pas été bien compris, je voulais justement dire que ton exemple de constructeur template (qui est valide) et le mien (qui est invalide, pas qu'inutilisable) n'était pas équivalent.

    De manière générale que ce soit pour les constructeurs ou pour les operateurs, je voulais signifier qu'il n'y a pas d'interet à laisser des parametres templates qui ne sont pas déterminé par les arguments. Ce serait donc dans l'interêt du prgorammeur de les interdire quand (et uniquement) dans ce cas là. Le constructeur par défaut n'est qu'un cas parmi d'autres.

  11. #11
    Membre habitué Avatar de nowahn
    Homme Profil pro
    Inscrit en
    Août 2008
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 84
    Points : 150
    Points
    150
    Par défaut
    Je relance le sujet.

    Koala01, je ne comprends pas le rapport de ton raisonnement avec mon sujet :
    Citation Envoyé par Koala01
    [...] templatiser l'opérateur d'affectation [...]
    [...]
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MaClass
    {
        pubic:
            /* [...] */
            /* opérateur d'affectation "classique" */
            MaClass & operator=(MaClass const & rhs){/*...*/ return *this;}
            /* opérateur d'affectation permettant d'écire MaClass c; c=1; */
            MaClass & operator=(int i){/*...*/ return *this;}
    };
    C’est pas de la templatisation ça, c’est de la surcharge, non ?
    Je précise que je comprends parfaitement tout ce à quoi tu fais allusion, mais je ne vois simplement pas le rapport avec ma question.
    Je crois que tu essayes de me trouver une alternative de conception à quelque chose que je voudrais faire et que je ferais mal, alors qu’ici, je ne cherche à rien faire, il n’y a pas d’erreur de conception, puisqu’il n’y a pas de conception.

    Sinon, je me permets de faire remarquer que, ayant conscience que ma question pouvait être mal interprétée (encore une fois, c’est une question purement technique, ne cherchez pas l’erreur de conception, il n’y en a pas puisqu’il n’y a pas de programme), j’avais fait l’effort de poser quatre questions précises et de les numéroter pour faciliter les réponses :
    Citation Envoyé par nowahn
    1. Quelqu’un sait comment appeler ce constructeur modèle ?
    2. Est-ce que le comportement de g++ est conforme à la norme ?
    3. Est-ce que d’autre compilateurs acceptent une de ces syntaxes d’appel (je n’en ai pas sous la main) ?
    4. Est-ce que la génération du constructeur par défaut dans un cas et pas dans l’autre (Class1 et Class3) est correct ou est-ce un bug de g++ ?
    Je n’ai eu la réponse à aucune de ces question.

    Je suppose que la réponse à la première est tout simplement « non », mais ça irait mieux en le disant.

    La réponse à la troisième question intéresse ma curiosité, le code que j’ai posté est directement utilisable (le deuxième dans mon premier message, il manque juste l’inclusion de <iostream>). Un copier-coller, une (tentative de ) compilation, et on a la réponse.

    Concernant la quatrième question, ce que j’aimerais savoir, c’est si je devrais rapporter un bug à l’équipe de développement de gcc.

    Voilà, c’était juste pour préciser les choses, encore une fois, il n’y a rien d’urgent (sauf peut-être pour la quatrième question, des fois que vous considéreriez le bug comme majeur, mais faudrait que vous soyez encore plus tordu que moi ).

    Merci d’avance.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par nowahn Voir le message
    Je relance le sujet.

    Koala01, je ne comprends pas le rapport de ton raisonnement avec mon sujet :

    C’est pas de la templatisation ça, c’est de la surcharge, non ?
    Il faut comprendre que, conceptuellement parlant, l'utilisation de template que tu envisage se rapproche très fortement... de la surcharge automatique de fonctions membres
    Je précise que je comprends parfaitement tout ce à quoi tu fais allusion, mais je ne vois simplement pas le rapport avec ma question.
    Le rapport est qu'il n'y a, tout simplement, aucun intérêt à envisager la templatisation d'une fonction si c'est pour ne pas utiliser l'argument type template.

    De plus, comme, dans ce cas d'utilisation, la templatisation s'apparente à de la surcharge automatique, tu restes soumis aux règles générales de la surcharge, à savoir que le nombre ou le type des arguments doit être différent pour chaque surcharge.

    Si tu ne respecte pas cette règle de base, tu fais simplement une erreur

    Je crois que tu essayes de me trouver une alternative de conception à quelque chose que je voudrais faire et que je ferais mal, alors qu’ici, je ne cherche à rien faire, il n’y a pas d’erreur de conception, puisqu’il n’y a pas de conception.
    Disons qu'il semble surtout y avoir une mauvaise compréhension de ta part de ce que l'on peut faire avec la programmation générique, et que je m'efforce de te présenter des cas qui te permettront de savoir quand il est effectivement intéressant d'y recourir

    Le paradigme générique est comme le paradigme (orienté) objets: C'est un paradigme super intéressant à utiliser, mais ce n'est pas forcément la solution ultime. Il faut savoir déterminer quand il vaut la peine d'y avoir recours
    Sinon, je me permets de faire remarquer que, ayant conscience que ma question pouvait être mal interprétée (encore une fois, c’est une question purement technique, ne cherchez pas l’erreur de conception, il n’y en a pas puisqu’il n’y a pas de programme), j’avais fait l’effort de poser quatre questions précises et de les numéroter pour faciliter les réponses :
    1. Quelqu’un sait comment appeler ce constructeur modèle ?
    2. Est-ce que le comportement de g++ est conforme à la norme ?
    3. Est-ce que d’autre compilateurs acceptent une de ces syntaxes d’appel (je n’en ai pas sous la main) ?
    4. Est-ce que la génération du constructeur par défaut dans un cas et pas dans l’autre (Class1 et Class3) est correct ou est-ce un bug de g++ ?


    Je n’ai eu la réponse à aucune de ces question.
    Au temps pour moi, j'ai zapé ces questions
    Je suppose que la réponse à la première est tout simplement « non », mais ça irait mieux en le disant.
    (1) Alors, disons le clairement: tu ne peux déjà pas fournir un constructeur templatisé si tu ne lui donne pas d'arguments, vu que c'est la seule solution pour utiliser le paramètre template, et que, sans argument, tu te heurte aux règles de surcharge des fonction.

    Vu que tu ne peux déjà pas créer un tel constructeur (bien que le compilateur ne le remarque pas d'office), tu ne peux fatalement pas l'appeler

    (2) Tout à fait: il refuse la surcharge de fonctions si le nombre ou le type des arguments (ou la constance de la fonction membre) n'est pas différent

    (3) Ce serait surprenant, la surcharge de fonctions entrant très certainement dans la liste de ce qui est le mieux respecté par l'ensemble des compilateurs

    (4)Je dirais que le constructeur par copie n'étant pas templatisé (d'ailleurs, un constructeur templatisé prenant une référence constante ou une valeur ne sera pas considéré comme constructeur par copie, mais simplement comme un constructeur "normal" (cf les citations que j'ai faites ), on peut estimer que class3 est légale
    La réponse à la troisième question intéresse ma curiosité, le code que j’ai posté est directement utilisable (le deuxième dans mon premier message, il manque juste l’inclusion de <iostream>). Un copier-coller, une (tentative de ) compilation, et on a la réponse.

    Concernant la quatrième question, ce que j’aimerais savoir, c’est si je devrais rapporter un bug à l’équipe de développement de gcc.
    Ce n'est pas forcément un bug
    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 habitué Avatar de nowahn
    Homme Profil pro
    Inscrit en
    Août 2008
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 84
    Points : 150
    Points
    150
    Par défaut
    Citation Envoyé par koala10
    Le rapport est qu'il n'y a, tout simplement, aucun intérêt à envisager la templatisation d'une fonction si c'est pour ne pas utiliser l'argument type template.
    Si tu entends « ne pas utiliser les arguments templates dans la liste des arguments de fonction », je ne suis pas tout à fait d’accord.
    C’est vrai que dans mes exemples, j’ai mis int comme paramètre de modèle, mais c’était pour que mes exemples restent cours. Il n’y a pas que la programmation générique dans la vie des templates, il y a aussi la méta-programmation. Le paramètre template peut très bien être un classe de politique.
    De plus, je n’ai parlé que des constructeurs sans arguments, mais le problème est le même pour les constructeurs avec arguments mais dont le paramètre template n’est pas utilisé dans les paramètres de fonction.
    Pour illustrer tout ça, voici un exemple qui gèrerait des heures, qui met en parallèle une fonction normale (MakePair) et un constructeur :
    Code C++ : 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
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    #include <iostream>
    #include <vector>
     
    // Politique qui vérifie que les éléments Hr et Mn sont compris entre 0 et 59.
    struct PolicyCheck
    {
       static std::pair<int, int> Check(std::pair<int, int> HrMn)
       {
          if(HrMn.first < 0)
             HrMn.first=0;
          else if(HrMn.first > 59)
             HrMn.first=59;
          if(HrMn.second < 0)
             HrMn.second=0;
          else if(HrMn.second > 59)
             HrMn.second=59;
       }
    };
     
    // Politique qui ne vérifie rien.
    struct PolicyNoCheck
    {
       static std::pair<int, int> Check(std::pair<int, int> HrMn)
       {
          return HrMn;
       }
    };
     
    // Déclaration d’une fonction paramétrée par les politiques.
    template<typnename _CheckPolicy>
    std::pair<int, int> MakeTimePair(int Hr, int Mn); // déclaration légale.
     
    // Classe encapsulant le concept d’heure.
    // Ne gère que les heures et les minutes.
    // Très incomplète, c’est juste pour l’exemple.
    class TTime
    {
    public:
       TTime();
       // déclaration d’un constructeur paramétré par les politiques.
       template<typename _CheckPolicy>
       TTime(int Hr, int Mn); // déclaration légale !
       // Je mets les données membres en public, je sais que c’est une grossière
       // erreur de conception, mais c’est pour simplifier le code de l’exemple.
       int Hr_;
       int Mn_;
    };
     
    TTime::TTime()
    :  Hr_(),
       Mn_()
    {
    }
     
    // Définition de la fonction paramétrée par les politiques.
    template<typename _CheckPolicy>
    std::pair<int, int> MakeTimePair(int Hr, int Mn) // Définition légale.
    {
       return _CheckPolicy::Check(std::make_pair(Hr, Mn));
    }
     
    // Définition du constructeur paramétré par les politiques.
    template<typename _CheckPolicy>
    TTime::TTime(int Hr, int Mn) // Définition légale !
    {
       std::pair<int, int> Pair(_CheckPolicy::Check(std::make_pair(Hr, Mn)));
       Hr_=Pair.first;
       Mn_=Pair.second;
    }
     
    // Instanciations explicites de la fonction paramétrée par les politiques.
    template
    std::pair<int, int> MakeTimePair<PolicyCheck>(int Hr, int Mn);
    template
    std::pair<int, int> MakeTimePair<PolicyNoCheck(int Hr, int Mn);
    // Instanciations explicites légales.
     
    // Instanciations explicites du constructeur paramétré par les politiques.
    template
    TTime::TTime<PolicyCheck>(int Hr, int Mn);
    template
    TTime::TTime<PolicyNoCheck>(int Hr, int Mn);
    // Instanciations explicites légales !
     
    // Utilisation.
    int main()
    {
       // Utilisation de la fonction paramétrée par les politiques.
       std::vector<std::pair<int, int> > VectorPair;
       VectorPair.push_back(MakeTimePair<PolicyCheck>(75, 75));
       VectorPair.push_back(MakeTimePair<PolicyNoCheck>(75, 75));
       std::cout << VectorPair[0].first << ':' << VectorPair[0].second << '\n';
       std::cout << VectorPair[1].first << ':' << VectorPair[1].second << '\n';
       // Utilisation légale.
     
       // Utilisation du constructeur paramétré par les politiques.
       std::vector<TTime> VectorTime; // Nécessite que les TTime soient tous du
                                      // même type.
       VectorTime.push_back(TTime<PolicyCheck>(75, 75)); // Illégal.
       VectorTime.push_back(TTime<PolicyNoCheck>(75, 75)); // Illégal.
       std::cout << VectorTime[0].Hr_ << ':' << VectorTime[0].Mn_ << '\n';
       std::cout << VectorTime[1].Hr_ << ':' << VectorTime[1].Mn_ << '\n';
       // Utilisation illégale ? Ou je ne connais pas la syntaxe d’appel ?
     
       return 0;
    }
    Pourquoi ce qui est possible avec une fonction classique ne le serait pas avec un constructeur ?
    Cet exemple n’est certainement pas la présentation la plus « propre » des classes de politique, mais c’est juste pour illustrer la question.
    Je sais que dans la plupart des cas similaires, c’est la classe entière qui est paramétrée avec la politique, c’est d’ailleurs obligatoire par exemple dans le cas de la politique d’allocation des conteneurs standards, puisque la politique utilisée dans le constructeur doit l’être dans toute la suite de la vie du conteneur. Mais je ne me sentais pas l’expertise de pouvoir affirmer qu’il n’y aurait pas de cas où il serait intéressant de limiter l’utilisation de la politique au constructeur.

    Bref, pour revenir à la question initiale de ce sujet, si j’ai posé cette question tordue, c’est parce que du haut de ma (très) petite expérience, j’estimais que je ne savais pas si de tels constructeurs pouvaient être utiles, indispensable, stupides, farfelus, tordus, ...
    Donc j’ai essayé d’en écrire un, et ayant réussi à le déclarer, le définir et l’instancier explicitement, j’ai supposé qu’il était possible de les utiliser, mais que je ne trouvais simplement pas la syntaxe pour les appeler.
    La tournure de cette conversation commence à me faire changer d’avis sur ce dernier point.

    Pour clarifier les choses, je vois quatre situations possibles :
    1. Ces constructeurs sont explicitement interdits par la norme.
      Dans ce cas, gcc est buggé puisqu’il permet de le déclarer, le définir et l’instancier explicitement (et peut-être de l’appeler si je suis passé à côté de la bonne syntaxe).
    2. Ces constructeurs sont explicitement autorisés par la norme.
      Dans ce cas, deux situations possibles :
      1. Je n’ai simplement pas trouvé la syntaxe pour les appeler.
      2. gcc est buggé car il permet bien de les déclarer, les définir et les instancier explicitement, mais pas de les appeler.
    3. Le cas de ces constructeurs n’a pas été envisagé par les rédacteurs de la norme.

    Donc, la toute première question que je posais au début (quand je supposais qu’on était dans le cas 2a), c’est :
    • Quelle est la syntaxe pour appeler ces constructeurs ?

    Et au vue de la tournure de la conversation, je la transforme en :
    • Dans laquelle des quatre situations ci-dessus sommes-nous ?


    PS : Notez que le même problème se pose pour le destructeur, mais la norme indique explicitement qu’il ne peut y avoir qu’un seul destructeur, ce qui exclut la possibilité de le templétiser, et d’ailleurs gcc donne un message d’erreur tout a fait explicite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    error: destructor ‘TClass::~TClass()’ declared as member template
    Donc dans le cas du destructeur, nous somme clairement dans le cas 1.
    Les fonctions normales sont clairement dans le cas 2.
    Le cas des constructeurs est pour moi ambigu.

  14. #14
    Membre habitué Avatar de nowahn
    Homme Profil pro
    Inscrit en
    Août 2008
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 84
    Points : 150
    Points
    150
    Par défaut
    Finalement, ce type de constructeur n’est peut-être pas si tordu que ça, y en a d’autres qui veule en faire, voir ici.

  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
    Bonjour nowahn

    Je n'avais pas lu en détail ton problème et les différentes réponses. Je débarque un peu en retard.
    Le problème de ton code sur la classe de gestion des heures est que tu essaies d'appeler un constructeur template explicitement, ce qui est illégale. La solution est simplement d'ajouter un "unamed parameters" dans le constructeur, qui n'aura aucune autre fonction que de permettre au compilateur de déduire le type de constructeur a appeler :

    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
    class TTime
    {
    public:
       TTime();
    
       template<typename _CheckPolicy>
       TTime(int Hr, int Mn, const _CheckPolicy &);
    
       int Hr_;
       int Mn_;
    };
    
    int main()
    {
       std::vector<TTime> VectorTime;
       VectorTime.push_back(TTime(75, 75, PolicyCheck())); // ok.
       VectorTime.push_back(TTime(75, 75, PolicyNoCheck())); // ok.
       std::cout << VectorTime[0].Hr_ << ':' << VectorTime[0].Mn_ << '\n';
       std::cout << VectorTime[1].Hr_ << ':' << VectorTime[1].Mn_ << '\n';
    
       return 0;
    }
    Par contre, il faudrait vérifier que ce paramètre supplémentaire soit bien supprimé par la compilation (puisqu'il est inutile à l'exécution) et ne diminue pas le temps d'exécution (même de rien du tout). A mon avis, non : le compilateur est assez intelligent pour supprimer du code binaire final les variables inutilisée... mais a vérifier.

  16. #16
    Membre habitué Avatar de nowahn
    Homme Profil pro
    Inscrit en
    Août 2008
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 84
    Points : 150
    Points
    150
    Par défaut
    @gbdivers :

    Dans ton code, il faudrait que PolicyCheck et PolicyNoCheck soient tous les deux des objets de type _CheckPolicy, et qu’il faudrait bien mettre un test if quelque part (dans la classe _CheckPolicy ou dans le constructeur qui la prend en paramètre), ce qui implique une surcharge à l’exécution.

    La solution pour que le choix du code se fasse à la compilation est la suivante :
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class TClass
    {
    public:
       template<typename _Type>
       TClass(_Type Policy);
    };
     
    // ...
     
    TClass Class(PolicyCheck());
    Bref, passer un objet de la classe politique en tant que paramètre de fonction au constructeur. Il me semble que ça doit être relativement simple à optimiser pour le compilateur pour supprimer toute surcharge d’exécution.
    EDIT: je pense que le code que j’ai mis sur l’autre sujet explique mieux ce que je veux dire.

    Cependant, ceci est une solution qui est tout aussi utilisable pour une fonction normale.
    Pour une fonction normale, on a deux solutions pour passer une politique, en argument de fonction ou en argument de modèle. Il me semble que la première nécessite des optimisation de la part du compilateur pour ne pas provoquer de surcharge à l’exécution, alors que la seconde n’en nécessite pas. De plus, la deuxième solution semble largement la plus utilisée dans le code existant (ce qui me paraît logique).
    Pour un constructeur, je ne trouve pas le moyen d’utiliser cette deuxième solution (il y a dans ce cas une troisième solution qui consiste à paramétrer la classe entière avec la politique, mais ça génère autant de types que de politiques, ce qui peut éventuellement être un problème.

    Je rappelle que ma question est au départ purement technique et syntaxique, sans aucune attache à un problème concret, j’ai pondu l’exemple de TTime vite fait pour illustrer que cette utilisation pourrait éventuellement être envisagée (je sais que dans cet exemple précis, il y a plusieurs solutions tout à fait satisfaisantes).

    Donc, pour m’auto-répondre à ma question purement technique, j’ai trouvé une solution pour appeler un tel constructeur, mais elle est très insuffisante :
    Code C++ : 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 <iostream>
     
    class TClass
    {
    public:
       template<typename _Type=int>
       TClass(bool B);
    };
     
    template<typename _Type>
    TClass::TClass(bool B)
    {
       std::cout << "TClass::TClass<_Type>()\n";
    }
     
    int main()
    {
       TClass Class(false);
       return 0;
    }
    Le paramètre bool est un paramètre quelconque, il est là car cette technique ne marche pas avec un constructeur sans arguments (de fonction). C’est la première limitation.
    Ce code n'est pas valide avec la norme actuelle du C++ (qui ne permet pas les paramètres de modèle par défaut pour les fonctions). Il faut le compiler selon le langage C++0x (option -std=c++0x de g++). C’est la deuxième limitation.
    Ce code ne permet d’appeler le constructeur modèle qu’avec son paramètre de modèle par défaut, ce qui enlève tout l’intérêt du template . C’est la troisième limitation.

    Mais en tout cas, ce code montre que ce constructeur modèle est bien généré comme n’importe quelle autre fonction. Il devrait donc pouvoir être appelé, d’une manière ou d’une autre.

    Citation Envoyé par nowahn
    Pour clarifier les choses, je vois quatre situations possibles :
    1. Ces constructeurs sont explicitement interdits par la norme.
      Dans ce cas, gcc est buggé puisqu’il permet de le déclarer, le définir et l’instancier explicitement (et peut-être de l’appeler si je suis passé à côté de la bonne syntaxe).
    2. Ces constructeurs sont explicitement autorisés par la norme.
      Dans ce cas, deux situations possibles :
      1. Je n’ai simplement pas trouvé la syntaxe pour les appeler.
      2. gcc est buggé car il permet bien de les déclarer, les définir et les instancier explicitement, mais pas de les appeler.
    3. Le cas de ces constructeurs n’a pas été envisagé par les rédacteurs de la norme.

    [...]
    • Dans laquelle des quatre situations ci-dessus sommes-nous ?
    Personne n’a la réponse à cette question ?
    Parce que si on est dans le dernier cas, c’est pas la peine de continuer cette discusion, elle ne sert à rien.
    (Remarquez que je conçoit que pas grand monde ne soit capable de répondre à cette dernière question, surtout si on est dans le dernier cas.)

  17. #17
    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
    Dans ton code, il faudrait que PolicyCheck et PolicyNoCheck soient tous les deux des objets de type _CheckPolicy, et qu’il faudrait bien mettre un test if quelque part (dans la classe _CheckPolicy ou dans le constructeur qui la prend en paramètre), ce qui implique une surcharge à l’exécution.
    Je ne comprend pas très bien. _CheckPolicy est un paramètre template, pas une classe (PolicyCheck et PolicyNoCheck n'hérite pas de _CheckPolicy).
    Ce que le compilateur fait, c'est de spéciliser
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TTime(int Hr, int Mn, const _CheckPolicy &);
    en créant 2 constructeurs différents pour TTime, en fonction du paramètre passé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    TTime(int Hr, int Mn, const PolicyCheck &);
    TTime(int Hr, int Mn, const PolicyNoCheck &);
    Il n'y a pas besoin de if dans la classe _CheckPolicy (qui n'existe même pas) ou dans TTime. Si on veut ajouter une nouvelle policy, il n'est pas besoin de modifier TTime.

    Ce que je t'ai donné, c'est toutes les modifications que j'ai eu besoin de faire pour que le code compile (et donc, je n'ai pas ajouté de if où que ce soit)

    Je pense que ce code correspond à ta question d'avoir un constructeur template (la seule différence est que la spécialisation se fait en fonction d'un paramètre de fonction plutôt qu'un paramètre de template explicite)

  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
    Draft n3126

    1/ Section 14.5.2 Note : Comme les constructeur n'ont pas de noms tu ne peux pas donner une liste explicite d'arguments templates. Donc tu peux déclarer (et définir) ton constructeur, mais tu pourras jamais l'utiliser.

    3/ Le constructeur par défaut n'est plus généré (en cas d'appel) dès que tu en déclare un qui peut être appellé sans argument (section 12.1).

    Dans ton cas il n'est pas généré, et je suppose ce comportement étrange lié à la syntaxe <Edit>cf le message de 3DArchi pour l'explication</Edit>, ce n'est pas lié au template, tu as "presque" la même chose sans template (sous gcc)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    struct T {  T(); };
     
    T t; //Erreur à l'édition des liens
    T t(T()); //Pas de problème
    T t = T(); //Erreur à l'édition des liens
    <Edit>Edition suite au message de 3DArchi</Edit>

  19. #19
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    struct T {  T(); };
     
    T t1; //Ne compile pas (logique)
    T t2(T()); //Compile !
    T t3 = T(); //Ne compile pas
    Tu as du oublié quelque chose car ce code compile (certes il ne linke pas et il y a 3 fois t comme nom de variable mais je suis persuadé que ce n'est pas ça que tu voulais dire)


    T t(T()); => T t(T (*)(void))

    Sinon, même lorsque le constructeur par défaut n'est pas généré, le constructeur par copie l'est lui.

  20. #20
    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
    Les trois lignes était à prendre une par une et pas les trois en même temps.

    En effet, j'aurais du mettre private à la place de déclarer sans définir, ca aurait été plus proche.

    Le but était juste de montrer que la ligne numéro 2 dont l'OP associait le comportement au template n'avait rien à voir avec les template.

    Dans le code de l'OP, le constructeur par défaut n'est pas généré et le constructeur qu'il définit est inutilisable, au final ca revient "presque" (*) (j'ai pas réfléchie au détails des différences) au même que de mettre le contructeur par défaut en privé, le déclarer sans le définir ou le déclarer delete, seul l'énoncé de l'erreur change. (et l'instant où elle apparait, édition des liens ou compilation).

    (*) Le but de telles manoeuvres serait en tout cas le même, rendre la classe non Default Constructible.

    T t(T()); => T t(T (*)(void));
    C'est donc bien la syntaxe et ca a rien à voir avec le constructeur de copie, je voyais pas ca comme ca quand même . Ca devrait répondre à la question de l'OP.

    Oui, je suis d'accord, ici le constructeur par copie ne pose pas de problème, il serait même généré si il y avait un "constructeur par copie" template.

Discussions similaires

  1. applet et constructeur sans argument
    Par new_wave dans le forum Applets
    Réponses: 2
    Dernier message: 09/05/2012, 08h58
  2. Pas de compilation sans constructeur sans argument!
    Par bertry dans le forum Débuter
    Réponses: 3
    Dernier message: 28/12/2011, 11h33
  3. Tableau et constructeurs sans arguments
    Par wafiwafi dans le forum Langage
    Réponses: 41
    Dernier message: 03/01/2010, 18h40
  4. [XSLT] - Modèle réutilisable... mais comment l'utiliser.
    Par jacquesh dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 17/02/2006, 11h07
  5. Signature d'une fonction sans argument
    Par cj227854 dans le forum C++
    Réponses: 5
    Dernier message: 20/10/2005, 17h01

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