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 :

Explicit vs Implicit constructor : un bug dans g++ ?


Sujet :

Langage C++

  1. #1
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut Explicit vs Implicit constructor : un bug dans g++ ?
    Bonjour.

    Ce post fait suite à une discussion sur SO et j'avoue que je ne comprends pas bien ce que dit le standard sur cette situation.

    Voilà le code incriminé :
    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>
    template<typename Type> class Base {};
    template<typename Type> class Other : public Base<Type> {};
    template<typename Type> class Derived : public Base<Type>
    {
        public:
            Derived() {std::cout<<"empty"<<std::endl;}
            Derived(const Derived<Type>& x) {std::cout<<"copy"<<std::endl;}
            template<typename OtherType> explicit Derived(const Derived<OtherType>& x) {std::cout<<"explicit"<<std::endl;}
            template<typename OtherType> Derived(const Base<OtherType>& x) {std::cout<<"implicit"<<std::endl;}
    };
    int main()
    {
        Other<int> other0;
        Other<double> other1;
        std::cout<<"1 = ";
        Derived<int> dint1;                     // <- empty
        std::cout<<"2 = ";
        Derived<int> dint2;                     // <- empty
        std::cout<<"3 = ";
        Derived<double> ddouble;                // <- empty
        std::cout<<"4 = ";
        Derived<double> ddouble1(ddouble);      // <- copy
        std::cout<<"5 = ";
        Derived<double> ddouble2(dint1);        // <- explicit
        std::cout<<"6 = ";
        ddouble = other0;                       // <- implicit
        std::cout<<"7 = ";
        ddouble = other1;                       // <- implicit
        std::cout<<"8 = ";
        ddouble = ddouble2;                     // <- nothing (normal : default assignment)
        std::cout<<"\n9 = ";
        ddouble = Derived<double>(dint1);       // <- explicit
        std::cout<<"10 = ";
        ddouble = dint2;                        // <- implicit : WHY ?!?!
        return 0;
    }
    Le LWS est ici : http://liveworkspace.org/code/cd423f...b843732d837abc

    La ligne qui me pose problème est l'avant dernière (celle où j'ai marqué WHY ?!?!)

    Petite explication :
    Mon but est :
    - d'autoriser la conversion implicite de n'importe quelle Base<T1> en Derived<T>
    - de bloquer la conversion implicite de Derived<T1> en Derived<T>
    - mais d'autoriser une conversion explicite de Derived<T1> en Derived<T>

    Le problème est que à la ligne incriminée, g++ passe par le constructeur implicite, moins spécialisé que le constructeur explicite. D'après le standard a-t-il le droit ? Ou est-ce un bug ?

    Dans le cas où tout est OK vis à vis du standard, quelle est la solution la plus simple (idéalement en ne modifiant que le design de Derived, sans toucher à Base, ni Other et sans rajouter de classe (une solution à base de enable_if par exemple)) ?

    EDIT : je rajoute ce "workaround" que je viens de trouver à l'instant. Ca implique seulement l'ajout d'un "faux" constructeur dans Derived. Cela semble-t-il une bonne façon de procéder (+ est-ce que tous les compilateurs sont susceptibles de donner le résultat que j'attends) ?
    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
    49
    50
    #include <iostream>
    #include <type_traits>
    template<typename Type> class Base {};
    template<typename Type> class Other : public Base<Type> {};
    template<typename Type> class Derived : public Base<Type>
    {
        public:
            Derived() {std::cout<<"empty"<<std::endl;}
            Derived(const Derived<Type>& x) {std::cout<<"copy"<<std::endl;}
            template<typename OtherType> explicit Derived(const Derived<OtherType>& x) {std::cout<<"explicit"<<std::endl;}
            template<typename Something> Derived(const Something& x) {std::cout<<"implicit"<<std::endl;}
     
        // Workaround
        public:
            template<template<typename> class Something, typename OtherType,
            class = typename std::enable_if< std::is_same< Something<OtherType>, Derived<OtherType> >::value>::type >
            Derived(const Something<OtherType>& x)
            {std::cout<<"workaround (for example always false static assert here)"<<std::endl;}
    };
    template<unsigned int Size> class Test {};
     
    int main()
    {
        Other<int> other0;
        Other<double> other1;
        Test<3> test;
        std::cout<<"1 = ";
        Derived<int> dint1;                     // <- empty
        std::cout<<"2 = ";
        Derived<int> dint2;                     // <- empty
        std::cout<<"3 = ";
        Derived<double> ddouble;                // <- empty
        std::cout<<"4 = ";
        Derived<double> ddouble1(ddouble);      // <- copy
        std::cout<<"5 = ";
        Derived<double> ddouble2(dint1);        // <- explicit
        std::cout<<"6 = ";
        ddouble = other0;                       // <- implicit
        std::cout<<"7 = ";
        ddouble = other1;                       // <- implicit
        std::cout<<"8 = ";
        ddouble = ddouble2;                     // <- nothing (normal : default assignment)
        std::cout<<"\n9 = ";
        ddouble = Derived<double>(dint1);       // <- explicit
        std::cout<<"10 = ";
        ddouble = dint2;                        // <- workaround
        std::cout<<"11 = ";
        ddouble = test;                         // <- implicit
        return 0;
    }

  2. #2
    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
    Pour ce qui est de la validité du code ça me semble correct :
    • La fonction selectioné est Derived<double>& Derived<double>::operator=(const Derived<double>&); généré par le compilateur.
    • Cet appel a besoin d'un séquence de conversion implicite de Derived<int>& vers const Derived<double>&, seulement Derived<double>:erived<double>(const Base<double>&) convient (pas d'autre possibilité).


    Pour ton problème, tu fais l'expérience du monstre constitué de l'héritage et la sémantique de copie.

  3. #3
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    Pour ton problème, tu fais l'expérience du monstre constitué de l'héritage et la sémantique de copie.
    A l'origine ma classe de base est un std::iterator et ma classe dérivée un itérateur qui prend des paramètres template supplémentaires. Je ne sais pas trop comment je pouvais m'y prendre autrement...

  4. #4
    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
    Tu peux détailler un peu ce que représente tes classes héritant de std::iterator ? Ils sont attaché à des conteneurs/flux ? Si c'est le cas quel est le rapport entre deux de ces conteneurs/flux ayant des types templates différents ?

  5. #5
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    En fait c'est un itérateur qui me permet de me balader dans un maillage. Les paramètres passés en template permettent de spécifier certaines propriétés de la façon dont l'itération va se passer : sens d'itération, gestion des raffinements, iteration uniquement sur les éléments de maillage qui vérifient certaines propriétés. Comme ces propriétés sont indépendantes, coder N itérateurs différents avec différents "mix" de paramètres aurait été trop lourd : je préfère donc spécifier ces propriétés d'itération par paramètre template.

    Je cherche donc à ce que l'itérateur :
    - puisse faire une conversion implicite depuis un raw pointer ou un std::iterator
    - ne puisse pas faire de conversion implicite depuis un itérateur perso qui aurait d'autres paramètres (trop dangereux)
    - mais autoriser une conversion explicite (avec gestion des changement de paramètres) si l'utilisateur est conscient de ce qu'il fait

  6. #6
    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
    D'accord, je ne vois pas pourquoi tu veux autoriser une conversation quelconque, c'est, AMA, inutile :
    • std::iterator sert juste à factoriser du code, il ne devrait pas apparaître comme argument de fonction.
    • Les pointeurs sont des itérateurs. Les itérateurs sont attachés à un conteneur/flux et définissent un parcours. Si ton itérateur n'est pas un pointeur, alors la conversion de l'un à l'autre n'a pas trop de sens.
    • C'est la même chose pour tes itérateurs perso, quel sens donnes tu à passer d'un parcours à l'autre ?

  7. #7
    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
    Salut,
    Assez d'accord avec Flob90.
    Après la description de ton problème me fait penser à un besoin de pattern décorateur.
    cf Le décorateur pour une description.
    Pour une implémentation boost Iterator Adaptor. Eventuellement voir les autres itérateurs boost (transform, filter, zip).


    edit :
    un petit chipotage :
    Citation Envoyé par Flob90 Voir le message
    Les itérateurs sont attachés à un conteneur/flux et définissent un parcours.
    Les itérateurs sont plus un 'concept'. Ils peuvent ne pas être rattachés à un conteneur/flux (expl Boost.CountingIterator, Boost.function_[input/output]_iterator même si ces 2 derniers ont qqs problèmes)

Discussions similaires

  1. Réponses: 4
    Dernier message: 16/12/2013, 01h20
  2. Bug dans le TCheckListBox ?
    Par Tardiff Jean-François dans le forum Composants VCL
    Réponses: 6
    Dernier message: 04/11/2004, 08h39
  3. Bug dans les expressions régulières ?
    Par SergioF dans le forum Linux
    Réponses: 8
    Dernier message: 12/05/2004, 15h14
  4. [PROPERTIES] Bug dans java.util.Properties ?
    Par mathieu dans le forum Collection et Stream
    Réponses: 6
    Dernier message: 28/04/2004, 15h11
  5. bug dans une base Access
    Par bizouard dans le forum Access
    Réponses: 5
    Dernier message: 29/12/2003, 12h41

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